Что такое C # эквивалент MsgWaitForMultipleObjects? - PullRequest
10 голосов
/ 22 февраля 2010

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

Я бы сделал это в собственном win32, чтобы создать рабочий поток для каждого элемента (наивно; конечно, я не буду создавать неограниченное количество потоков), а затем MsgWaitForMultipleObjects () в массиве дескрипторов потоков. Когда каждое вычисление заканчивается, сигнал потоков и основной поток пользовательского интерфейса просыпаются и обновляются. В то же время мы прокачиваем сообщения, чтобы поток пользовательского интерфейса оставался отзывчивым.

Может ли кто-нибудь привести пример того, как это может работать в C #? Я посмотрел на объект «Монитор», и он, кажется, не тот, который я хочу, или он качает сообщения во время блокировки?

Спасибо.

Редактировать: Кажется, что WaitHandler.WaitAny () может на самом деле качать сообщения. См. трактат cbrumme о перекачке сообщений в CLR.

Ответы [ 2 ]

2 голосов
/ 22 февраля 2010

Сделайте, чтобы ваш главный поток создал поток менеджера. Вы можете использовать BackgroundWorker для этого. Этот поток менеджера запускает рабочий поток для каждого элемента в ListView. Это позволит вашему пользовательскому интерфейсу продолжать отвечать на вводимые пользователем данные без зависаний, пока обрабатываются фоновые потоки.

Теперь проблема в том, как ждать завершения каждого рабочего потока. К сожалению, мне не удалось найти способ получить дескриптор потока для System.Threading.Thread объектов. Я не говорю, что нет способа сделать это; Я просто не нашел ни одного. Еще один сложный аспект этого заключается в том, что класс System.Threading.Thread запечатан, поэтому мы не можем извлечь из него какой-то «дескриптор».

Здесь я использую ManualResetEvent .

Скажем, каждый рабочий поток - это просто ThreadPool поток. Управляющий BackgroundWorker создает объект ManualResetEvent для каждого элемента в ListView. Когда BackgroundWorker запускает каждый поток ThreadPool, передайте ManualResetEvent в качестве аргумента функции QueueUserWorkItem . Затем, перед каждым выходом из потока ThreadPool, установите объект ManualResetEvent.

Поток BackgroundWorker может затем поместить все объекты ManualResetEvent в массив и ожидать этого массива, используя WaitHandle.WaitXXX функции . По завершении каждого потока вы можете использовать события BackgroundWorker для обновления пользовательского интерфейса или метод Control.Invoke() для обновления пользовательского интерфейса (см. Ответ Марка Гравелла здесь ).

Надеюсь, это поможет.

2 голосов
/ 22 февраля 2010

Длительный активный объект, я думаю, является лучшим выбором в вашем случае. Основной поток вызывает прокси (активного объекта). Прокси преобразует метод вызова в сообщение, и это сообщение отправляется в очередь. Прокси возвращает вызывающему объекту будущий объект (это ссылка на будущий результат). Диспетчер выводит сообщения один за другим и действительно выполняет вашу задачу в другом потоке (рабочем потоке). Когда рабочий поток завершает задачу, он обновляет результат будущего объекта или вызывает метод обратного вызова (например, для обновления вашего пользовательского интерфейса). У диспетчера может быть много рабочих потоков для одновременного выполнения более одной задачи.

Вы можете увидеть эту статью (с образцом) о продолжительном образце активного объекта.

...