繁体   English   中英

如何同步这两个线程?

[英]How can I synchronize these two threads?

这是课程:

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循环之前放一个锁,然后只有一个线程在运行,而另一个线程被锁定。 把锁放在别处不会改变任何东西。

我认为您不能相信sleep(300)来保持线程独立运行相同的次数...

您可以做的一件事是拥有一个中央计时器/滴答生成器,该信号在每个滴答中发出一个同步对象信号,并且线程函数仅滴答一次,然后WaitsForObject从主线程生成下一个滴答,实际上有一个计时器和告诉线程同步滴答。

还要注意,订阅线程函数事件的方式需要在处理函数中考虑竞争条件。 每个方法都将在其自己的线程上运行(直到begininvoke),因此,如果您访问任何资源(类字段等),则这些资源需要同步。 忘记线程发生了什么太容易了。 :(

如果您确实需要它们完全同步并按一定顺序执行刻度,则需要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少的开销)来计时。

使用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();
        }
    }
}

好吧,如果您使用的是.NET 4.0,则可以使用Barrier,但是您必须将其放入Ticker类中,否则会阻塞UI线程。

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

在您的股票报价器类中,增加轮询频率并检查系统计时器,直到达到所需的时间间隔为止。 如果可以毫秒精度运行,则可以使用TickCountTicks;如果系统支持,可以使用StopWatch以获得更高的精度。

为了使它们保持同步,它们在开始时需要一个公共参考。 你可以通过这个作为未来特定剔开始同步或使用类似Tick模量100或轮询为表示何时开始共享静态标志。

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

可以帮助您解决的一件事是启动一个共享的静态StopWatch实例,并在所有日志记录中回显其经过的时间,以帮助您确定应用程序中的任何延迟。

暂无
暂无

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

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