Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
321 views
in Technique[技术] by (71.8m points)

c# - Synchronised threads coroutines

I'm trying to get threads to wait for each other before preceding so they stay in sync.

In my actual program I have lots of IObjectObserved objects (on their own threads) sending out events and I want to keep everything in sync so an IObjectListener (on its own thread) can listen to one of these objects 50 times and then subscribe to another in time to catch its 51st event.

I haven't got that far yet, but I think synchronizing threads is the main problem. I'm managed to achieve this with two way signalling using AutoResetEvents. Is there not a better way to do this?

class Program
{
    static EventWaitHandle _ready = new AutoResetEvent(true);
    static EventWaitHandle _go = new AutoResetEvent(false);
    static EventWaitHandle _ready1 = new AutoResetEvent(true);
    static EventWaitHandle _go1 = new AutoResetEvent(false);
    static EventWaitHandle _ready2 = new AutoResetEvent(true);
    static EventWaitHandle _go2 = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        new Thread(Waiter).Start();
        new Thread(Waiter1).Start();
        new Thread(Waiter2).Start();
        for (; ; )
        {
            _ready.WaitOne();
            _ready1.WaitOne();
            _ready2.WaitOne();
            Console.WriteLine("new round");
            _go.Set();
            _go1.Set();
            _go2.Set();
        }
    }

    static void Waiter()
    {
        for (; ; )
        {
            _go.WaitOne();
            Thread.Sleep(1000);
            Console.WriteLine("Waiter run");
            _ready.Set();
        }
    }
    static void Waiter1()
    {
        for (; ; )
        {
            _go1.WaitOne();
            Thread.Sleep(5000);
            Console.WriteLine("water1 run");
            _ready1.Set();
        }
    }
    static void Waiter2()
    {
        for (; ; )
        {
            _go2.WaitOne();
            Thread.Sleep(500);
            Console.WriteLine("water2 run");
            _ready2.Set();
        }
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You could simplify things a bit.

  • You could use one CountdownEvent instead of waiting for 3 handles to be signaled. cde.Wait will block until it has been signaled 3 times.
  • You could also use a SemaphoreSlim to release multiple threads, instead of using 3 different handles. sem.Release(3) will unblock up to 3 threads blocked on sem.Wait().


static CountdownEvent cde = new CountdownEvent(3);
static SemaphoreSlim sem = new SemaphoreSlim(3);

static void X()
{
    new Thread(Waiter).Start();
    new Thread(Waiter).Start();
    new Thread(Waiter).Start();
    for (; ; )
    {
        cde.Wait();
        Debug.WriteLine("new round");

        cde.Reset(3);
        sem.Release(3);
    }
}

static void Waiter()
{
    for (; ; )
    {
        sem.Wait();
        Thread.Sleep(1000);
        Debug.WriteLine("Waiter run");
        cde.Signal();
    }
}

Note that now all threads can reuse the same code.


Edit:

As stated in the comments, one thread might steal another thread's round. If you don't want this to happen, a Barrier will do the job:

   private static Barrier _barrier;

   private static void SynchronizeThreeThreads()
   {
       _barrier = new Barrier(3, b =>
                                Debug.WriteLine("new round"));

       new Thread(Waiter).Start();
       new Thread(Waiter).Start();
       new Thread(Waiter).Start();

       //let the threads run for 5s
       Thread.Sleep(5000);
   }
   static void Waiter()
   {
       while(true)
       {
           Debug.WriteLine("Thread {0} done.", Thread.CurrentThread.ManagedThreadId);
           _barrier.SignalAndWait();
       }
   }

You add 3 participants to the barrier. The first and second participants to reach the barrier (i.e., to execute _barrier.SignalAndWait()) will block until the remaining participants reaches it too. When all participants have reached the barrier, they will all be released and go for another round.

Notice that I passed a lambda to the barrier constructor - that's the "post-phase action", an action that will be executed after all participants have reached the barrier, and before releasing them.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...