Нужно объяснение win API "Асинхронные вызовы процедур" относительно .NET - PullRequest
0 голосов
/ 30 апреля 2011

У меня есть ошибка. В приложении WinForm .NET2.0 моя HeavyFunction может вызываться из обработчиков событий щелчка мыши. Итак, когда я начинаю быстрый щелчок, у меня происходит сбой при записи файла из-за того, что HeavyFunction введена во второй раз.

Мое решение. Я вспомнил вещи относительно APC и решил синхронизировать вещи честно. То есть Я хочу исключить эффекты APC в HeavyFunction. Итак, я использовал ThreadPool. Для синхронизации я попытался использовать SyncLock (Visual Basic). И не удалось. Мьютексы тоже не работают. Я просто не могу понять, как моя HeavyFunction может вызываться из одного и того же потока дважды за раз. Но это так. Я думал, что если я использую ThreadPool, то заставляю каждый вызов выполняться в другом потоке.

Определение APC http://msdn.microsoft.com/en-us/library/ms681951%28v=vs.85%29.aspx.
Вот «Справедливость синхронизации потоков в CLR» Джеффри Рихтера (меня тошнит): http://codeguru.earthweb.com/csharp/.net/net_general/threads/article.php/c4647/Thread-Synchronization-Fairness-in-the-CLR.htm

РЕДАКТИРОВАТЬ: Кажется, я нашел причину исключения аварии. Это антивирус AVG. Вероятно, он считает подозрительной деятельность, когда кто-то щелкает очень быстро, и это вызывает быстрые операции открытия / записи файла. Или это сильно замедляет мой компьютер. В любом случае я не могу воспроизвести сбой, когда AVG отключен. Хотя ProcMon показывает, что AVG имеет доступ к файлу, пока он включен.

Наконец, кто-нибудь может объяснить мне APC? А именно, может ли APC привести к двойному входу в одну и ту же функцию в одном потоке? Я понял, что это может из этого текста :
«Асинхронный вызов процедуры (APC) - это функция, которая выполняется асинхронно в контексте определенного потока. Когда APC ставится в очередь в поток, система выдает программное прерывание. В следующий раз, когда поток запланирован, он выполнит Функция APC. "

Я боюсь умереть без этого понимания.

Ответы [ 5 ]

2 голосов
/ 30 апреля 2011
class yourform {
   Queue<somedata> queue = new Queue<somedata>();
   Thread caller;   
   private bool Closing {get;set;}

   public yourform() {
     /// initcomponents bla bla
     Thread t = new Thread((ThreadStart)delegate() {
        while (!Closing) {
          lock (queue) {
             if (queue.Peek()) {
               somedata data = queue.Dequeue();
               HeavyFunction(data); // add invoke if required
             }
          }
        }
     }).Start();
     caller = t;
   }

   ~yourform() {
     Closing = true;
     caller.Join();
   }
   void clickEventHandler(object sender, EventArgs e) {
     somedata data = new somedata();
     queue.Queue(data);
   }
}

Редактировать: вы не пометили Visual Basic, поэтому я не заметил, но вы можете использовать онлайн переводчик vb to c # ...

2 голосов
/ 30 апреля 2011

Мне не ясно, хотите ли вы одновременно выполнять много вызовов ... вы могли просто отключить кнопку во время выполнения тяжелой функции?

EDIT

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

1 голос
/ 01 мая 2011

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

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

APC - красная сельдь.Они могут «одолжить» поток в ожидании с предупреждением (которое среда выполнения .NET обычно использует для заблокированных потоков пользовательского интерфейса), но APC не будет вызывать обработчик событий пользовательского интерфейса (по крайней мере, я не могу представить ни одной ситуации, которая могла бы вызвать это).

1 голос
/ 30 апреля 2011

В своем комментарии вы говорите, что хотите, чтобы события обрабатывались одно за другим в порядке их поступления. Это звучит как очередь.Посмотрите на этот вопрос: Как повторно использовать потоки в .NET 3.5 Принятый ответ показывает реализацию рабочей очереди.Если вы используете его с workerCount = 1, то у вас есть очередь, обрабатываемая одним потоком, и события будут обрабатываться в порядке их поступления в очередь.

0 голосов
/ 30 апреля 2011

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

Я не уверен, что происходит, но если ваш обработчик событий щелчка мышисоздает новый объект (с HeavyFunction), тогда блокировка вам не поможет.

Доступна ли HeavyFunction один и тот же файл каждый раз?Если это так, то пул потоков также не будет работать, потому что все потоки все еще будут пытаться получить доступ к одному файлу.

Я думаю, что лучшее решение - использовать очередь, как предлагает ChrisWise.

...