Подтвердить что ты не робот

В С# каков рекомендуемый способ передачи данных между двумя потоками?

У меня есть мой основной поток графического интерфейса, а второй поток работает внутри него собственного ApplicationContext (чтобы поддерживать его, даже когда нет работы, которая должна быть выполнена). Я хочу вызвать метод на моем втором потоке из моего потока графического интерфейса, но если я просто вызову thread.Method(); похоже, работает на моем главном потоке GUI и заставляет мой графический интерфейс становиться невосприимчивым. Каков наилучший способ вызова методов для разных потоков?

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

Обновление # 2: Хорошо, я действительно должен что-то пропускать. Я создал событие и делегат, и мой рабочий поток подписался на это событие. Но когда я вызываю Invoke (MyEvent); из моего потока GUI работа, которую рабочий поток заканчивает тем, что находится в потоке GUI, и висит в графическом интерфейсе, пока не завершит обработку. Является ли то, что я пытаюсь сделать даже возможным, без опроса на статическом объекте?

4b9b3361

Ответ 1

.Net уже поставляется с классом System.ComponentModel.BackgroundWorker, специально предназначенным для обработки фоновых задач и общения с графическим интерфейсом. Используйте его.

Ответ 2

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

В любом случае, это то, что я делаю.

  • Создайте классы "сообщение". Это сохраняет всю информацию, которую вы хотите предоставить.
  • Создать очередь <T> для каждого потока. Используйте SyncLock (блокировка С#) для чтения/записи на него.
  • Когда вы хотите поговорить с потоком, отправьте ему объект сообщения с копировать всю необходимую информацию, добавив сообщение в очередь.
  • Рабочий поток затем может считывать из очереди, считывая и обрабатывая каждое сообщение по порядку. Когда сообщений нет, просто спать.

Убедитесь, что вы не обмениваетесь объектами между двумя потоками. После того, как ваш поток GUI вставляет сообщение в очередь, поток GUI больше не владеет этим сообщением. Он не может содержать ссылку на сообщение, или вы попадете в неприятности.

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

ОБНОВЛЕНИЕ. Не используйте SyncLock и Queue. Вместо этого используйте ConcurrentQueue, который автоматически обрабатывает любую блокировку для вас. Вы получите лучшую производительность и вряд ли допустите ошибку.

Ответ 3

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

Лучше всего делать то, что вы задумали, и просто использовать .NET ThreadPool и дать ему работу.

Ответ 4

Чувак, читайте Albahari.Net, набирая бесплатную электронную книгу. Я подключен к нему любым способом, так что это не плагин. Я прочитал его, если бы мои сотрудники прочитали его, и я использовал его много раз.

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

просто Google для него.

Ответ 5

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



//delegate with same prototype as the method to call asynchrously
delegate void ProcessItemDelegate(object item);

//method to call asynchronously
private void ProcessItem(object item) { ... }

//method in the GUI thread
private void DoWork(object itemToProcess)
{
    //create delegate to call asynchronously...
    ProcessItemDelegate d = new ProcessItemDelegate(this.ProcessItem);
    IAsyncResult result = d.BeginInvoke(itemToProcess,
                                        new AsyncCallback(this.CallBackMethod),
                                        d); 
}

//method called when the async operation has completed
private void CallbackMethod(IAsyncResult ar)
{
    ProcessItemDelegate d = (ProcessItemDelegate)ar.AsyncState;
    //EndInvoke must be called on any delegate called asynchronously!
    d.EndInvoke(ar);
}

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

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


//shared state
private Queue workQueue;
private EventWaitHandle eventHandle;

//method running in gui thread
private void DoWork(Item itemToProcess)
{
   //use a private lock object instead of lock...
   lock(this.workQueue)
   {
       this.workQueue.Add(itemToProcess);
       this.eventHandle.Set();
   }
}

//method that runs on the background thread
private void QueueMonitor()
{
   while(keepRunning)
   {
       //if the event handle is not signalled the processing thread will sleep here until it is signalled or the timeout expires
       if(this.eventHandle.WaitOne(optionalTimeout))
       {
           lock(this.workQueue)
           {
              while(this.workQueue.Count > 0)
              {
                 Item itemToProcess = this.workQueue.Dequeue();
                 //do something with item...
              }
           }
           //reset wait handle - note that AutoResetEvent resets automatically
           this.eventHandle.Reset();
       }
   }
}

Ответ 6

Удобство Control.BeginInvoke() трудно упустить. Вам не обязательно. Добавьте новый класс в проект и вставьте этот код:

using System;
using System.Threading;
using System.Windows.Forms;

public partial class frmWorker : Form {
  public frmWorker() {
    // Start the worker thread
    Thread t = new Thread(new ParameterizedThreadStart(WorkerThread));
    t.IsBackground = true;
    t.Start(this);
  }
  public void Stop() {
    // Synchronous thread stop
    this.Invoke(new MethodInvoker(stopWorker), null);
  }
  private void stopWorker() {
    this.Close();
  }
  private static void WorkerThread(object frm) {
    // Start the message loop
    frmWorker f = frm as frmWorker;
    f.CreateHandle();
    Application.Run(f);
  }
  protected override void SetVisibleCore(bool value) {
    // Shouldn't become visible
    value = false;
    base.SetVisibleCore(value);
  }
}

Вот пример кода для его проверки:

  public partial class Form1 : Form {
    private frmWorker mWorker;
    public Form1() {
      InitializeComponent();
      mWorker = new frmWorker();
    }

    private void button1_Click(object sender, EventArgs e) {
      Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
      mWorker.BeginInvoke(new MethodInvoker(RunThisOnThread));
    }
    private void RunThisOnThread() {
      Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId);
    }

    private void button2_Click(object sender, EventArgs e) {
      mWorker.Stop();
    }
  }

Ответ 7

Используйте объект синхронизации, чтобы сигнализировать поток, необходимый для обработки новых данных (или нового состояния графического интерфейса пользователя). Один из относительно простых способов сделать это - использовать объект события. Вот как это работает:

  • Поток GUI, второй поток совместно используют объект события (поэтому они оба знают об этом)
  • 2-й поток обычно работает в каком-то цикле, и каждый раз, когда он ожидает сообщения о событии
  • Поток GUI сигнализирует событие, когда ему нужен второй поток, чтобы что-то сделать
  • Когда выполняется второй поток, он сбрасывает событие и снова ждет (или выходит)

Ответ 8

Поместите цикл во второй поток, который больше всего спит, но каждый [Интервал] он просыпается и проверяет общую переменную, которая сообщает ему, следует ли запускать ваш метод или нет, и если для этого общего логического значения установлено значение true, тогда он запускает метод, который выполняет любую задачу, которую вы пытаетесь выполнить... В этом методе метод собирает данные, необходимые из другой общей переменной.

В главном потоке GUI поместите данные в общую переменную параметра метода, затем установите для общей логической переменной "Run" значение true...

Внутри рабочего метода запомните reset переменную "run" с общим значением "bool" до "false", когда вы закончите, так что цикл выиграл; t запускает один и тот же экземпляр снова и снова...

Ответ 9

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

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