Сценарий: глобальные переменные в DLL, которые используются многопоточным приложением.

Несколько месяцев назад я наткнулся на интересный сценарий, заданный парнем (в orkut). Хотя я придумал «непереносимое» решение этой проблемы (протестировал его с небольшим кодом), но все же хотел бы знать, что вы, ребята, можете сказать и предложить.

Предположим, я создал DLL, экспортируя некоторые функции, написанные на C++, для однопоточного клиента. Эта DLL объявляет множество глобальных переменных, некоторые могут быть константными переменными (только для чтения), а другие могут быть изменены.

В любом случае, позже все изменилось, и теперь я хочу, чтобы та же DLL работала с многопоточным приложением (без модификации DLL); это означает, что несколько потоков получают доступ к функциям и глобальным переменным из DLL и изменяют их... и так далее. Все это может привести к тому, что глобальные переменные будут содержать несогласованные значения.

Итак, вопрос в том,

Можем ли мы сделать что-то в клиентском коде, чтобы предотвратить множественный доступ к DLL и в то же время гарантировать, что каждый поток работает в своем собственном контексте (то есть, когда он получает доступ к DLL, глобальные значения DLL такие же, как и раньше)?


person Nawaz    schedule 30.11.2010    source источник
comment
Если вам разрешено хотя бы перестроить dll, вы можете добавить __declspec(threadlocal) ко всем своим глобальным определениям. Я сильно подозреваю, что это приведет к сбою на любых объектах класса со статической областью видимости с конструкторами или деструкторами.   -  person Chris Becke    schedule 30.11.2010
comment
Крис... нет, мы не можем добавить __declspec(threadlocal) к глобальным переменным, так как изменение исходного кода не разрешено. :-)   -  person Nawaz    schedule 30.11.2010


Ответы (2)


Можем ли мы сделать что-то в клиентском коде, чтобы предотвратить множественный доступ к DLL и в то же время гарантировать, что каждый поток работает в своем собственном контексте (это означает, что когда он получает доступ к DLL, глобальные значения DLL совпадают с это было раньше)?

Это сложная часть. Я думаю, что единственный способ сделать это - создать оболочку вокруг существующей DLL. Когда он вызывается, он восстанавливает состояние (глобальные переменные) для текущего потока и сохраняет их при возврате из вызова DLL. Вам нужно будет знать все переменные состояния в DLL и уметь их читать/записывать.

Если производительность не является проблемой, будет достаточно одной блокировки для всей DLL, и это будет проще всего правильно реализовать. Это гарантировало бы, что только один поток одновременно обращается (читает или записывает) к DLL.

person KeithB    schedule 30.11.2010

Конечно, вы всегда можете создать слой-оболочку, обрабатывающий определенные многопоточные задачи, такие как блокировка. Вы даже можете сделать это во второй DLL, которая связана с исходной, а затем связать окончательный проект с этой новой DLL.

Имейте в виду, что независимо от того, как вы это реализуете, это не будет легкой задачей. Вы должны точно знать, какой поток может изменять какое значение и в какое время, кто может читать, что и когда и т. д., если вы не хотите столкнуться с такими проблемами, как взаимоблокировки или условия гонки.

Если ваше решение позволяет это, часто лучше назначить один поток для изменения любых данных, а все остальные просто читать и никогда не записывать, так как параллельный доступ для чтения всегда проще реализовать, чем параллельный доступ для записи (Boost предоставляет для этого все основные функции, например shared_mutex).

person Mephane    schedule 30.11.2010
comment
Мефан, ваш последний абзац имеет смысл, и на самом деле я сделал что-то подобное в своем решении, но без использования Boost API. кстати спасибо за ответ. :-) - person Nawaz; 30.11.2010
comment
Кстати, понимаете ли вы, что значения глобальных переменных, скорее всего, будут разными для разных потоков, и поэтому всякий раз, когда один поток получает доступ к DLL (при этом блокируя доступ других потоков), он должен иметь те же значения глобальных переменных, что и раньше , означает, что этот текущий поток должен запускаться из той же точки/состояния. - person Nawaz; 30.11.2010
comment
Извините, я забыл спросить вас, обрабатывает ли Boost API случай (разные значения для разных потоков и все такое), объясненный в предыдущем комментарии? - person Nawaz; 30.11.2010
comment
@Nawaz: это усложняет картину, не так ли? Если ваша dll statefull, ваша задача кажется совершенно невозможной без изменения кода. Вам нужно либо хранилище для каждого потока, либо изменить интерфейс, чтобы он поддерживал сеансы клиента. - person davka; 30.11.2010
comment
@Nawaz: Почему значения global должны быть разными, если только какой-то поток не изменяет их? И если вы хотите, чтобы каждый поток получал «нетронутую» копию этих данных, вы всегда можете запустить все потоки с копией этих исходных, неизмененных данных. Однако проблема возникает, когда вы хотите именно этого - несколько потоков обращаются к одним и тем же данным с точки зрения идентичности, а не равенства значений. Или я неправильно понял ваш вопрос? - person Mephane; 30.11.2010
comment
@ Мефан .... Нет. Вы, наверное, правильно меня поняли. Я хочу, чтобы несколько потоков обращались к одним и тем же глобальным переменным, однако их значения будут разными для разных потоков. Как вы выразились, одни и те же данные с точки зрения идентичности, а не ценности равенства. - person Nawaz; 30.11.2010
comment
Нет, если вы обращаетесь к идентичным глобальным переменным, значения не будут (могут) отличаться для другого потока. Они будут другими, чем раньше, но с теми же новыми значениями для всех, как только кто-то их изменит. Их значения будут различаться не в зависимости от того, кто к ним обращается, а в зависимости от того, были ли они вообще изменены, независимо от того, когда и кем произошло это изменение. - person Mephane; 01.12.2010