Доступ к переменным-членам класса внутри обработчика событий BackgroundWorker's DoWork и другие ограничения BackgroundWorker - PullRequest
1 голос
/ 18 марта 2010

Вопрос 1

Безопасен ли доступ к (для чтения и записи) переменным-членам класса, который содержит BackgroundWorker, в обработчике событий DoWork в BackgroundWorker? Безопасно ли обращаться к другим переменным, которые не объявлены внутри самого обработчика событий DoWork?

Очевидно, что DoWork не должен обращаться к каким-либо объектам пользовательского интерфейса, скажем, приложения WinForms, поскольку пользовательский интерфейс должен обновляться только из потока пользовательского интерфейса. Но как насчет доступа к другим (не связанным с пользовательским интерфейсом) переменным-членам?

Причина, по которой я спрашиваю, состоит в том, что я видел случайный комментарий, когда Гуглинг говорил, что доступ к переменным-членам не разрешен. Единственный пример, который я могу найти на данный момент, - это комментарий на этой странице MSDN , в котором говорится:

Обратите внимание, что BGW может вызывать исключения, если он пытается получить доступ или изменить переменные уровня класса. Все данные должны быть переданы ему делегатами и событиями.

А также:

НИКОГДА. НИКОГДА. Никогда не пытайтесь ссылаться на переменные, не объявленные внутри DoWork. Может показаться, что время от времени это работает, но на самом деле вам просто везет.

Насколько я знаю, сам MSDN не документирует никаких ограничений такого рода (хотя, если я ошибаюсь, я был бы признателен за ссылку). Но подобные комментарии, похоже, появляются время от времени.

(Конечно, если DoWork выполняет доступ / изменяет переменную-член, которая может быть доступна / изменена основным потоком одновременно, необходимо синхронизировать доступ к этому полю, например, с помощью объекта блокировки. Но выше кавычки, кажется, требуют полного запрета доступа к переменным-членам, а не просто синхронизации доступа!)

Вопрос 2

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

Ответы [ 3 ]

1 голос
/ 18 марта 2010

Комментарии, которые вы цитируете в вопросе 1, являются неправильными . С точки зрения CLR, вы можете получить доступ к членам класса формы из вашего BackgroundWorker (за исключением, как вы говорите, элементов управления, потому что они имеют сходство с потоками; но доступ к элементам не-Control, таким как целые числа, в порядке). Да, как вы заметили, если вы сделаете это, то вам нужно правильно синхронизировать доступ: это всегда в многопоточном сценарии. Но плохая синхронизация не вызовет исключений, как предполагает первый комментатор: она просто приведет к повреждению старых добрых данных (что, конечно, намного лучше!). И неверно говорить, что вам «просто везет». Это не вопрос удачи; это вопрос хорошей синхронизации.

Почему я квалифицирую это замечание как "с точки зрения CLR"? Во-первых, многопоточный доступ к состоянию затруднен. Таким образом, хотя это не проблематично для CLR, это может быть проблематично для человека, программирующего CLR. Во-вторых, потому что, если ваш класс формы содержит много не-пользовательского интерфейса, который требуется для BackgroundWorker, это может указывать на то, что приложение плохо структурировано. Это может означать, что эти вещи должны быть упакованы в объект, и BackgroundWorker должен вызывать метод для этого объекта, а не возиться с состоянием объекта Form.

0 голосов
/ 18 марта 2010

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

В пределах DoWork доступ к переменным-членам (также известным как поля) происходит из рабочего потока. Первая проблема заключается в том, что переменная-член обновляется для ссылки на нуль или другой объект некоторым потоком после запуска фонового работника. Если это возможно, уместна синхронизация (ключевое слово synclock в C # или другом механизме). Вторая проблема заключается в том, что, хотя ссылка может быть стабильной, сам объект не может быть потокобезопасным. Если класс является частью .NET Framework, документация для класса почти всегда объясняет, является ли он потокобезопасным. Если это не является потокобезопасным, добавление синхронизации вокруг доступа к переменной-члену, которая ссылается на объект, и использования самого объекта часто решает эту проблему.

Особенно обратите внимание на переменные-члены, которые ссылаются на объекты графического интерфейса Windows Forms (например, элементы управления). Хотя эти ссылки обычно стабильны, как правило, эти объекты не являются поточно-ориентированными. Хуже того, synclock не решит проблему, потому что к объектам Windows Forms предъявляется специальное требование, чтобы вызовы этих объектов выполнялись в потоке графического интерфейса, изначально связанном с объектами. Для выполнения вызовов этих объектов можно использовать методы объекта Invoke или BeginInvoke (определенные в классе Control), чтобы заставить код запускаться в потоке GUI для объекта. В качестве альтернативы, обновления графического интерфейса могут быть помещены в события ProgressChanged и RunWorkerCompleted BackgroundWorker: в отличие от DoWork, эти события возникают в потоке графического интерфейса.

Многопоточность, как известно, полна ловушек. BackgroundWorker помогает упростить некоторые вещи, но многие из подводных камней остаются. Надеемся, вы понимаете, что глубокое понимание многопоточного программирования важно для интеграции BackgroundWorker в ваши приложения.

0 голосов
/ 18 марта 2010

Если переменная является членом события, все в порядке.

Если переменная является членом, скажем, в форме, в которой создан этот BackgroundWorker, я думаю, вам следует заблокировать его перед чтением или записью значения, даже для примитивных типов, таких как целые числа, логические значения, строки и т. Д.

Помните, что коллекции .NET 3.5 не являются потокобезопасными.

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

...