2010-12-20 48 views
1

这里是类:如何同步这两个线程?

public class Ticker 
{ 
    public event EventHandler Tick; 
    public EventArgs e = null; 
    public void TickIt() 
    { 
     while (true) 
     { 
      System.Threading.Thread.Sleep(300); 
      if (Tick != null) 
      { 
       Tick(this, e); 
      } 
     } 
    } 

我运行在Windows窗体两个线程:

public partial class Form1 : Form 
{ 
    Ticker ticker1 = new Ticker(); 
    Ticker ticker2 = new Ticker(); 
    Thread t; 
    Thread t1; 

    public Form1() 
    { 
     InitializeComponent(); 
     ticker1.Tick += ticker1_Tick; 
     ticker2.Tick += ticker2_Tick; 

     t = new Thread(new ThreadStart(ticker1.TickIt)); 
     t1 = new Thread(new ThreadStart(ticker2.TickIt))); 
     t.Start(); 
     t1.Start(); 

    } 
    public void ticker1_Tick(object sender, EventArgs e) 
    { 
     if (this.InvokeRequired) 
     { 
      this.BeginInvoke((MethodInvoker)delegate 
      { 
       ticker1_Tick(sender, e); 
      }); 
      return; 
     } 


     richTextBox1.Text += "t1 "; 
    } 
    public void ticker2_Tick(object sender, EventArgs e) 
    { 
     if (this.InvokeRequired) 
     { 
      this.BeginInvoke((MethodInvoker)delegate 
      { 
       ticker2_Tick(sender, e); 
      }); 
      return; 
     } 


     richTextBox2.Text += "t2 "; 
    } 

的问题是后几秒钟线程t超前T1的几个蜱。

首先为什么会发生这种情况,它没有任何意义,因为每个线程在滴答之前应该等待300毫秒?

其次,我怎样才能同步这两个线程,所以他们同时打勾,一个不领先于另一个?

我不能在while循环之前放置一个锁,那么只有一个线程将会运行,而另一个线程将被锁定。在别处放置一个锁并不会改变任何东西。

+2

是你Tick()方法线程安全....? – 2010-12-20 02:15:00

+0

我不确定,如果不是,我不知道如何让它变得安全。 Tick是一个事件:公共事件EventHandler Tick; – JohnCoSystem 2010-12-20 02:22:43

+1

您应该显示整个可执行程序。请参阅[简短,独立,正确(可编译),示例](http://sscce.org/)中的指导原则。 – 2010-12-20 02:26:35

回答

2

我不认为你可以信任的睡眠(300),让您的线程运行相同的次数独立...

有一两件事你可以做的是有一个中央计时器/滴答发生器在每个滴答信号上发出一个同步对象,并且线程函数只滴答一次,然后WaitsForObject用于从主线程生成下一个滴答,实际上有一个定时器并告诉线程同步地进行滴答。

另请注意,您订阅线程函数事件的方式,您需要考虑处理函数中的竞争条件。每个方法都会在它自己的线程上运行(直到begininvoke),所以如果你访问任何资源(类字段等),那么这些都需要同步。忘记这些线索会发生什么很容易。 :(

+0

可否请您详细说明您的答案中最后一段的含义,谢谢。 – JohnCoSystem 2010-12-20 03:07:29

+0

更新了答案.... – Jaime 2010-12-20 03:16:18

2

如果你真的需要它们完全同步并按照一定的顺序执行刻度,那么你需要一些类似Jaime提到的中央计时器。如果你需要独立的计时,但是想要防止由于睡眠不准确造成漂移,或者延迟增加到执行事件处理程序所需的时间时,类似这样的事情就会起作用:

public class Ticker 
{ 
    public event EventHandler Tick; 
    public EventArgs e = null; 
    public void TickIt() 
    { 
     const int targetSleepTime = 300; 
     int nextTick = Environment.TickCount + targetSleepTime; 
     while (true) 
     { 
      System.Threading.Thread.Sleep(Math.Max(nextTick - Environment.TickCount, 0)); 
      if (Tick != null) 
      { 
       Tick(this, e); 
      } 
      nextTick += targetSleepTime; 
     } 
    } 
} 

只要记住Environment.TickCount在到达Int32.MaxValue时可以返回到Int32.MinValue。您需要额外的代码来处理这个问题,或者可能将时间基于DateTime.UtcNow(比DateTime.Now更少的开销)。

2

如何使用AutoResetEvent

class Program 
{ 
    static readonly AutoResetEvent thread1Step = new AutoResetEvent(false); 
    static readonly AutoResetEvent thread2Step = new AutoResetEvent(false); 

    static void Main(string[] args) 
    { 
     new Thread(new ThreadStart(Thread1Main)).Start(); 
     new Thread(new ThreadStart(Thread2Main)).Start(); 
    } 

    private static void Thread1Main() 
    { 
     for (int i = 0; i < int.MaxValue; i++) 
     { 
      Console.WriteLine("thread1 i=" + i); 
      thread1Step.Set(); 
      thread2Step.WaitOne(); 
     } 
    } 

    private static void Thread2Main() 
    { 
     for (int i = 0; i < int.MaxValue; i++) 
     { 
      Console.WriteLine("thread2 i=" + i); 
      thread2Step.Set(); 
      thread1Step.WaitOne(); 
     } 
    } 
} 
0

在您的Ticker类中,增加您的轮询频率并检查系统计时器,直到达到您要查找的时间间隔。如果您可以以毫秒精度生活,则可以使用TickCountTicks,如果系统支持它,则可以使用StopWatch以获得更高精度。

为了使它们保持同步,他们需要一个启动时间的通用参考。您可以将其作为特定的未来时间点传入,以开始同步或使用类似Tick模数100的内容。或者轮询共享静态标志,表示何时开始。

您不能拥有绝对精度,因此从一开始就定义您可以使用的精度范围,例如您的Ticker线程之间的正负5ms。

有一件事会帮助你启动一个共享静态的实例,并在你所有的日志中回复它的运行时间,以帮助你描述应用程序中的任何延迟。