2012-01-18 2 views
1

может кто-нибудь помочь мне, как установить метод Thread.join() в моем классе или если есть аккуратный способ работы с классом SynchronizationContext и thread.join. в основном, я пытаюсь обновить ячейку datagridview (dgv) и индикатор выполнения (pb) каждые 2 секунды из другого потока (а не потока пользовательского интерфейса). функциональность отлично работает, когда один поток выполняет задание; однако я хотел бы установить 2 потока, чтобы первый поток (Thread 1) обновил элементы управления (в моем случае он обновит datagridview и отобразит 10 строк, а индикатор выполнения будет обновлен до 50%). как только поток 1 выполнит свою работу, Thread 2 должен запустить и обновить элементы управления (в моем случае он обновит datagridview и отобразит еще 10 строк, а индикатор выполнения будет обновлен до 100%). См. Код ниже.Поперечное взаимодействие C#

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

namespace DelegatesAndCallback 
{ 
public partial class Form1 : Form 
{ 
    private Thread newThread1; 
    private Thread newThread2; 

    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 

     int id = Thread.CurrentThread.ManagedThreadId; 
     Trace.WriteLine("Button thread "+id); 

     SynchronizationContext uiContext = SynchronizationContext.Current; 

     // thread #1 
     startThread1(uiContext); 

     // thread #2 
     startThread2(uiContext); 
    } 

    public void startThread1(SynchronizationContext sc) 
    { 
     // thread #1 
     newThread1 = new Thread(Process1) { Name = "Thread 1" }; 
     newThread1.Start(sc); 
     //newThread1.Join(); 
    } 

    public void startThread2(SynchronizationContext sc) 
    { 
     // thread #2 
     newThread2 = new Thread(Process2) { Name = "Thread 2" }; 
     newThread2.Start(sc); 
     //newThread2.Join(); 
    } 

    public void updateProgressBarValue(object state) 
    { 
     double val = Convert.ToDouble(state)/19.0; 
     pb.Value = (int)(100*val); 
    } 

    public void updateDataGridViewValue(object state) 
    { 
     dgv.Rows.Add((int) state, (int) state); 
    } 

    public void Process1(object state) 
    { 
     SynchronizationContext uiContext = state as SynchronizationContext; 

     for (int i = 0; i < 10; i++) 
     { 
      uiContext.Send(updateDataGridViewValue, i); 
      uiContext.Send(updateProgressBarValue, i); 
      Thread.Sleep(2000); 
     } 
    } 

    public void Process2(object state) 
    { 
     SynchronizationContext uiContext = state as SynchronizationContext; 

     for (int i = 10; i < 20; i++) 
     { 
      if (uiContext != null) uiContext.Send(updateProgressBarValue, i); 
      if (uiContext != null) uiContext.Send(updateDataGridViewValue, i); 
      Thread.Sleep(2000); 
     } 
    } 
} 
} 
+0

Что именно вы с просьбой помочь с? Какие проблемы возникают при использовании thread.join? Зачем вам два потока? Можете ли вы начать второй поток с первого? – Chris

+0

@ Крис, ты прав Крис, если я добавлю второй поток в первый, он будет работать. Тем не менее, я пытаюсь использовать метод thread.join таким образом, что, как только первый поток заканчивает свое задание, второй поток начинает свою работу. –

+0

Проблема в том, что thread.join Я считаю, что останавливает выполнение текущего потока, ожидая окончания другого потока. Таким образом, в вашем примере, если вы раскомментировали '.Join()' s, вы бы не сильно отличались от того, чтобы просто делать это в одном потоке (есть некоторые различия, но не в плане простого потока программы). – Chris

ответ

1

Чтобы синхронизировать темы, вы должны использовать [Manual | Auto] ResetEvents. Вы должны использовать другие шаблоны для написания безопасного кода. Исследовать мой код пожалуйста:

public interface IProgress 
{ 
    ManualResetEvent syncEvent { get; } 
    void updateProgressBarValue(int state); 
    void updateDataGridViewValue(int state); 
} 

public partial class Form1 : Form, IProgress 
{ 
    // Sync object will be used to syncronize threads 
    public ManualResetEvent syncEvent { get; private set; } 

    public Form1() 
    { 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     // Creeate sync object in unsignalled state 
     syncEvent = new ManualResetEvent(false); 

     // I like Async model to start background workers 
     // That code will utilize threads from the thread pool 
     ((Action<IProgress>)Process1).BeginInvoke(this, null, null); 
     ((Action<IProgress>)Process2).BeginInvoke(this, null, null); 
    } 

    public void updateProgressBarValue(int state) 
    { 
     // InvokeRequired? -> Invoke pattern will prevent UI update from the non UI thread 
     if (InvokeRequired) 
     { 
      // If current thread isn't UI method will invoke into UI thread itself 
      Invoke((Action<int>)updateProgressBarValue, state); 
      return; 
     } 

     double val = Convert.ToDouble(state)/19.0; 
     pb.Value = (int)(100 * val); 
    } 

    public void updateDataGridViewValue(int state) 
    { 
     if (InvokeRequired) 
     { 
      Invoke((Action<int>)updateDataGridViewValue, state); 
      return; 
     } 

     dgv.Rows.Add((int)state, (int)state); 
    } 

    public void Process1(IProgress progress) 
    { 
     for (int i = 0; i < 10; i++) 
     { 
      // We have InvokeRequired in the methods and don't need any other code to invoke it in UI thread 
      progress.updateDataGridViewValue(i); 
      progress.updateProgressBarValue(i); 
      Thread.Sleep(2000); 
     } 

     // When thread 1 complete its job we will set sync object to signalled state to wake up thread 2 
     syncEvent.Set(); 
    } 

    public void Process2(IProgress progress) 
    { 
     // Thread 2 will stop until sync object signalled 
     syncEvent.WaitOne(); 

     for (int i = 10; i < 20; i++) 
     { 
      progress.updateProgressBarValue(i); 
      progress.updateDataGridViewValue(i); 
      Thread.Sleep(2000); 
     } 
    } 
} 

Кодекс был обновлен для вызова обновления UI из разных классов

+0

Ваш код работает, но мне просто хотелось бы узнать, добавляю ли я новый процесс (процесс 3) с помощью функции syncEvent.WaitOne() ', похоже, не работает. не могли бы вы просветить меня? –

+0

Если вам нужно запустить Thread 3 в то же время, что и Thread 2, он будет работать правильно. Но если вам нужно запустить Thread 3, когда Thread 2 завершит свою работу, вы должны использовать другой экземпляр объекта синхронизации для его реализации. –

+0

Ваш подход работает, thx много –

2

См Control.Invoke(), который специально разработан, чтобы нити не-UI взаимодействовать с вещами, как прогресс баров. В этом случае использование Invoke заменит ваш контекст синхронизации и ваш метод Send().

На несколько связанной ноте: гораздо более простой способ, чтобы создать нить:

new Thread(
() => { 
    /// insert code you want executed in a separate thread here... 
    } 
).Start(); 

Update Если вам необходимо обновить индикатор из другого класса, я мог бы сделать что-то вроде этого :

public partial class Form1 : Form 
{ 
    private ThreadOwner _threadOwner; 

    public Form1() 
    { 
     InitializeComponent(); 
     var _threadOwner = new ThreadOwner(); 
     _threadOwner.StartAThread(this,progressBar1.Minimum,progressBar1.Maximum); 
    } 

    protected override void OnClosing(CancelEventArgs e) 
    { 
     _threadOwner.Exit(); 

     base.OnClosing(e); 
    } 

    internal void SetProgress(int progress) 
    { 
     if (progressBar1.InvokeRequired) 
     { 
      progressBar1.Invoke(
       new Action<Form1>(
        (sender) => { 
         SetProgress(progress); 
        } 
        ),new[] { this } 
        ); 

     } 
     else 
      progressBar1.Value = progress; 
    } 
} 

А класс ThreadOwner:

public class ThreadOwner 
{ 
    private bool _done = false; 

    public void StartAThread(Form1 form, int min, int max) 
    { 
     var progress = min; 

     new Thread(() => 
      { 
       while (!_done) 
       { 
        form.SetProgress(progress); 

        if (progress++ > max) 
        { 
         progress = min; 
        } 
       } 

      } 
     ).Start(); 
    } 

    internal void Exit() 
    { 
     _done = true; 
    } 
} 

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

+0

Не вопрос о Thread.join? Это, конечно, как это начинается, хотя я признаю, что я немного смутился о том, что вопрос действительно прошел частично ... – Chris

+0

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

+0

@ user945511 В вашем примере все находится в одном классе. Если вам нужно обновить индикатор выполнения в отдельном классе, выведите метод в своей форме, который вызовет потоки, который затем выполняет Invoke(). Другие классы не должны напрямую ссылаться на элементы управления на форме в любом случае. –

Смежные вопросы