我怎样才能同步这两个线程?

发布于 2024-10-08 20:04:43 字数 1636 浏览 0 评论 0原文

这是类:

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 循环之前加一把锁,这样只有一个线程会运行,而另一个线程被锁定。在其他地方加锁不会改变任何事情。

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:

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.

First of all why is this happening, it doesn't make sense, since each thread should wait 300 ms before ticking?

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. Putting a lock elsewhere doesn't change anything.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

晨光如昨 2024-10-15 20:04:43

如果您确实需要它们完全同步并按一定顺序执行滴答,您将需要某种中央计时器,正如 Jaime 提到的那样。如果您需要独立计时,但想要防止因 Sleep 不精确而导致的漂移,或者由于执行事件处理程序所需的时间而增加的延迟,则可以使用以下方法:

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.MinValue当它到达 Int32.MaxValue 时。您需要额外的代码来处理该问题,或者可能基于 DateTime.UtcNow 的计时(比 DateTime.Now 的开销更少)。

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. 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. You'll need extra code to handle that, or maybe base the timing on DateTime.UtcNow (less overhead than DateTime.Now).

被翻牌 2024-10-15 20:04:43

我认为您不能相信 sleep(300) 可以让您的线程独立运行相同的次数...

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

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

I don't think you can trust the sleep(300) to keep your threads running the same number of times independently...

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.

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. It's just too easy to forget what's going on with the threads. :(

反目相谮 2024-10-15 20:04:43

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

How about using 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();
        }
    }
}
戏舞 2024-10-15 20:04:43

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

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

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.

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

涫野音 2024-10-15 20:04:43

在您的 Ticker 类中,增加轮询频率并检查系统计时器,直到达到您想要的间隔。您可以使用 TickCount刻度如果您可以接受毫秒精度,或者使用 StopWatch如果您的系统支持的话,可以获得更高的精度。

为了使它们保持同步,它们需要一个共同的开始时间参考。您可以将其作为特定的未来刻度传递以开始同步,或者使用 Tick modulus 100 之类的东西。或者轮询表示何时开始的共享静态标志。

您无法拥有绝对的精度,因此从一开始就定义您可以接受的精度范围,例如 Ticker 线程之间的正负 5 毫秒。

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

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.

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.

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文