简体   繁体   中英

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:

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.

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. :(

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).

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

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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