C # Базовый вопрос о многопоточности: вызов метода в потоке A из потока B (поток B запущен из потока A) - PullRequest
1 голос
/ 18 марта 2010

Каков наилучший способ сделать это: основной поток (поток A) создает два других потока (поток B и поток C). Потоки B и C выполняют тяжелый дисковый ввод-вывод и, в конечном итоге, должны передать ресурсы, созданные ими, в поток A, чтобы затем вызвать метод во внешнем файле DLL, который требует, чтобы поток, создавший его, был вызван правильно, поэтому только поток A может вызвать его .

Единственный раз, когда я использовал потоки, был в приложении Windows Forms, и методы вызова были именно тем, что мне было нужно. Эта программа не использует Windows Forms, и поэтому в ней нет методов Control.Invoke для использования.

В своем тестировании я заметил, что если переменная создается в потоке A, у меня нет проблем с доступом и изменением ее из потока B / C, что мне кажется очень неправильным. С Winforms я был уверен, что он выдает ошибки при попытке доступа к вещам, созданным в других потоках. Я знаю, что менять вещи из нескольких потоков небезопасно, но я очень надеялся, что .NET вообще запретит это для обеспечения безопасного кодирования. Делает ли это .NET, и я просто скучаю по лодке, или она делает это только с приложениями WinForm?

Так как это, по-видимому, позволяет это сделать, я делаю что-то, как сделала бы ОС, создала флаг и отслеживала его из потока А, чтобы увидеть, если он изменится. Если это так, тогда вызовите метод. Разве обработчик событий, по сути, не делает этого, поэтому может ли событие использоваться как-то вызванным в главном потоке?

Ответы [ 4 ]

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

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

Вместо того, чтобы пытаться вызвать метод в другом потоке, обычно вы пытаетесь использовать синхронизацию (т. Е. Блокировку (...) и т. П.) Для защиты доступа к самим данным и обеспечения их безопасной работы. с из нескольких потоков.

Обеспечивает более тонкую защиту, которая хороша для производительности. Использование Control.Invoke на самом деле довольно дорого, так как использует обмен сообщениями Windows для пометки потока, который ему нужно запустить.

Как говорится, на самом деле это можно сделать, хотя и довольно сложно. «Хитрость» заключается в том, что вы можете создать объект со своим собственным потоком, и этот объект предоставит SynchronizationContext . Затем вы можете использовать SynchronizationContext.Post или Send для запуска методов в этом контексте.

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

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

Из моего pov проще всего будет работать с синхронизированной очередью, в которую B и C будут помещать элементы в очередь. Поток A затем отключится из очереди и, возможно, подождет, пока что-то новое будет поставлено в очередь.
Посмотрите на комментарий Дуга в этой статье MSDN в качестве простого примера такой очереди. Вы также можете поставить делегатов в очередь, чтобы убедиться, что код выполняется в нужном потоке.

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

Прежде всего, «Winforms» не выдает ошибки просто при попытке доступа к данным из другого потока. Это невозможно; нет никакого способа узнать, какой поток первоначально объявил некоторую переменную или создал некоторые данные. То, что делает , приводит к тому, что исключение в Winforms пытается вызвать методы на потомке Control из любого потока, кроме потока пользовательского интерфейса - это очень специфический особый случай. Кроме того, Winforms - это просто библиотека, работающая поверх той же самой среды выполнения, что и любое другое приложение .NET, и не имеет специального контроля над управлением потоками.

. Для .NET Framework или любой другой библиотеки абсолютно невозможно представить «безопасный» многопоточный код. Определенные концепции помогают уменьшить состояние гонки и другие ошибки параллелизма, такие как библиотека Concurrent Collections в .NET 4, неизменяемые типы данных и т. Д., Но вы сами должны их использовать. Когда вы пишете многопоточный код, вы берете на себя все риски и обязательства, связанные с ним. Программист должен вы гарантировать, что ваш код является поточно-ориентированным.

Конкретный случай, на который вы ссылаетесь, кажется довольно простым. Мне нравится использовать Thread Pool, поэтому я покажу вам решение с использованием событий; Вы также можете сделать это с фактическими Thread объектами и использовать Thread.Join. Предполагая, что мы находимся в потоке A здесь:

void Xyz()
{
    SomeData dataFromB = null;
    ManualResetEvent finishedB = new ManualResetEvent(false);
    ThreadPool.QueueUserWorkItem(s =>
    {
        dataFromB = GetDataFromB();
        finishedB.Set();
    });

    SomeOtherData dataFromC = null;
    ManualResetEvent finishedC = new ManualResetEvent(false);
    ThreadPool.QueueUserWorkItem(s =>
    {
        dataFromC = GetDataFromC();
        finishedC.Set();
    });

    finishedB.WaitOne();
    finishedC.WaitOne();
    finishedB.Dispose();
    finishedC.Dispose();

    CallExternalDLL(dataFromB, dataFromC);
}

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

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

"Я был уверен, что он выдал ошибки за попытку доступа к вещам, созданным в других потоках." Этот оператор действителен только для свойств элементов управления. Обобщенная переменная может быть доступна из любого потока. Да, это может вызвать проблемы (условия гонки), когда сделано неправильно, но иногда это необходимо. Вы должны пометить эти переменные как volatile, минимизировать доступ к ним, использовать блокировки или класс Interlocked. Это мои советы:)

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