简体   繁体   English

如何同步这两个线程?

[英]How can I synchronize these two threads?

here is the class: 这是课程:

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);
            }
        }
    }

I'm running two threads in the windows form: 我在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 ";
    }

The problem is after some seconds thread t is ahead of t1 by several ticks. 问题是线程t在几秒钟之后比t1提前了几秒钟。

First of all why is this happening, it doesn't make sense, since each thread should wait 300 ms before ticking? 首先,为什么会发生这种情况,这是没有意义的,因为每个线程应在滴答之前等待300毫秒?

Second, how can I sync these two threads, so they tick simultaneously and one doesn't get ahead of the other? 其次,我该如何同步这两个线程,以便它们同时进行跳动,而一个线程又不能领先另一个线程?

I can't put a lock before the while loop, then only one thread will be running, while the other is locked out. 我不能在while循环之前放一个锁,然后只有一个线程在运行,而另一个线程被锁定。 Putting a lock elsewhere doesn't change anything. 把锁放在别处不会改变任何东西。

I don't think you can trust the sleep(300) to keep your threads running the same number of times independently... 我认为您不能相信sleep(300)来保持线程独立运行相同的次数...

One thing you could do is to have a central timer/tick generator that signals a synchronization object on each tick, and the thread function only ticks once and then WaitsForObject for the next tick to be generated from the main thread, effectively having one timer and telling the threads to tick synchronously. 您可以做的一件事是拥有一个中央计时器/滴答生成器,该信号在每个滴答中发出一个同步对象信号,并且线程函数仅滴答一次,然后WaitsForObject从主线程生成下一个滴答,实际上有一个计时器和告诉线程同步滴答。

Also note that the way you are subscribing to the thread function event, you need to consider race conditions in your handler functions. 还要注意,订阅线程函数事件的方式需要在处理函数中考虑竞争条件。 Each method will run on it's own thread (until the begininvoke) so, if you access any resource (class fields etc.) those would need to be synchronized. 每个方法都将在其自己的线程上运行(直到begininvoke),因此,如果您访问任何资源(类字段等),则这些资源需要同步。 It's just too easy to forget what's going on with the threads. 忘记线程发生了什么太容易了。 :( :(

If you really need them to be perfectly in synch and execute the ticks in a certain order, you will need some kind of central timer as Jaime mentioned. 如果您确实需要它们完全同步并按一定顺序执行刻度,则需要Jaime提到的某种中央计时器。 If you need independent timing but want to prevent drift caused by Sleep being imprecise, or delay added by the time it takes to execute the event handler, something like this would work: 如果您需要独立的时序,但又想防止由于睡眠的不精确性或执行事件处理程序所花费的时间而增加延迟,则可以使用以下方法:

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;
        }
    }
}

Just keep in mind Environment.TickCount can wrap back to Int32.MinValue when it gets to Int32.MaxValue. 请记住,Environment.TickCount可以在返回到Int32.MaxValue时返回到Int32.MinValue。 You'll need extra code to handle that, or maybe base the timing on DateTime.UtcNow (less overhead than DateTime.Now). 您将需要额外的代码来处理该问题,或者可能基于DateTime.UtcNow(比DateTime.Now少的开销)来计时。

How about using AutoResetEvent ? 使用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();
        }
    }
}

Well you could use a Barrier if you're using .NET 4.0, but you would have to put it in your Ticker class otherwise you'll block your UI thread. 好吧,如果您使用的是.NET 4.0,则可以使用Barrier,但是您必须将其放入Ticker类中,否则会阻塞UI线程。

http://msdn.microsoft.com/en-us/library/system.threading.barrier.aspx http://msdn.microsoft.com/zh-CN/library/system.threading.barrier.aspx

In your Ticker class, increase your polling frequency and check the system timer until you hit the interval you're looking for. 在您的股票报价器类中,增加轮询频率并检查系统计时器,直到达到所需的时间间隔为止。 You can use TickCount or Ticks if you can live with millisecond precision, or use StopWatch for higher precision if your system supports it. 如果可以毫秒精度运行,则可以使用TickCountTicks;如果系统支持,可以使用StopWatch以获得更高的精度。

To keep them synchronized, they'll need a common reference for a start-time. 为了使它们保持同步,它们在开始时需要一个公共参考。 You can pass this in as a specific future tick to start syncing on or use something like Tick modulus 100. Or poll for a shared static flag that signifies when to start. 你可以通过这个作为未来特定剔开始同步或使用类似Tick模量100或轮询为表示何时开始共享静态标志。

You cannot have absolute precision, so define what precision range you can live with from the outset, such as plus-or-minus 5ms between your Ticker threads. 您不能具有绝对的精度,因此请从一开始就定义您可以使用的精度范围,例如,股票代号线程之间的正负5ms。

One thing that'll help is to start a shared static StopWatch instance and echo its elapsed time in all of your logging to help you characterize any delays in your app. 可以帮助您解决的一件事是启动一个共享的静态StopWatch实例,并在所有日志记录中回显其经过的时间,以帮助您确定应用程序中的任何延迟。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM