Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

wait for another thread

So I have this program that attempts to establish communication between two different threads, thread1 and thread2.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Project1
{
    class Class1
    {
        public static void thread1() 
        {
            Console.WriteLine("1");
            Console.WriteLine("t2 has printed 1, so we now print 2");
            Console.WriteLine("t2 has printed 2, so we now print 3");
        }

        public static void thread2()
        {
            Console.WriteLine("t1 has printed 1, so we now print 1");
            Console.WriteLine("t1 has printed 2, so we now print 2");
            Console.WriteLine("t1 has printed 3, so we now print 3");
        }

        public static void Main() {

            Thread t1 = new Thread(new ThreadStart(() => thread1()));
            Thread t2 = new Thread(new ThreadStart(() => thread2()));
            t1.Start();
            t2.Start();
            t2.Join();
            t1.Join();

        }

    }
}

However, I want it to occur such that this line:

Console.WriteLine("1");

...gets executed first, while thread2 merely waits for this line to be executed. Then and only then would it print:

Console.WriteLine("t1 has printed 1, so we now print 1");

After this line is printed, then and only then would this line:

Console.WriteLine("t2 has printed 1, so we now print 2");

...get printed, and so on. So I want to change the code, so that the threads to communicate with each other so that the lines get printed in this order:

            Console.WriteLine("1"); // from t1
            Console.WriteLine("t1 has printed 1, so we now print 1"); // from t2
            Console.WriteLine("t2 has printed 1, so we now print 2"); // from t1
            Console.WriteLine("t1 has printed 2, so we now print 2"); // from t2
            Console.WriteLine("t2 has printed 2, so we now print 3"); // from t1
            Console.WriteLine("t1 has printed 3, so we now print 3"); // from t2

I understand what lock does, but it only applies if the two different threads are running on the same function. However, here, the two functions are different and thus I can't use lock here.

Any ideas?

like image 848
Richard Chen Avatar asked Dec 04 '22 14:12

Richard Chen


2 Answers

It looks like you need Monitor.Wait and Monitor.Pulse. There's a free eBook out there on Threading (there are probably many but this one helped me).

You can use a static object to lock on, and then have your threads call Monitor.Pulse to signal that they are "done with their turn" and Monitor.Wait to "wait for their next turn". Here's an example implementation using your basic code:

public class Class1
{
     // Use this to syncrhonize threads
    private static object SyncRoot = new object();
     // First "turn" goes to thread 1
    private static int threadInControl = 1;

    public static void thread1()
    {
        lock(SyncRoot) // Request exclusive access to SyncRoot
        {
            Console.WriteLine("1");
            GiveTurnTo(2); // Let thread 2 have a turn
            WaitTurn(1);   // Wait for turn to be given to thread 1
            Console.WriteLine("t2 has printed 1, so we now print 2");
            GiveTurnTo(2); // Let thread 2 have a turn
            WaitTurn(1);   // Wait for turn to be given to thread 1
            Console.WriteLine("t2 has printed 2, so we now print 3");
            GiveTurnTo(2); // Let thread 2 have a turn
        }
    }

    public static void thread2()
    {
        lock(SyncRoot) // Request exclusive access to SyncRoot
        {
            WaitTurn(2);   // Wait for turn to be given to thread 2
            Console.WriteLine("t1 has printed 1, so we now print 1");
            GiveTurnTo(1); // Let thread 1 have a turn
            WaitTurn(2);   // Wait for turn to be given to thread 2
            Console.WriteLine("t1 has printed 2, so we now print 2");
            GiveTurnTo(1); // Let thread 1 have a turn
            WaitTurn(2);   // Wait for turn to be given to thread 2
            Console.WriteLine("t1 has printed 3, so we now print 3");
            GiveTurnTo(1); // Let thread 1 have a turn
        }
    }

    // Wait for turn to use SyncRoot object
    public static void WaitTurn(int threadNum)
    {
        // While( not this threads turn )
        while (threadInControl != threadNum)
        {
            // "Let go" of lock on SyncRoot and wait utill 
            // someone finishes their turn with it
            Monitor.Wait(SyncRoot);
        }
    }

    // Pass turn over to other thread
    public static void GiveTurnTo(int nextThreadNum)
    {
        threadInControl = nextThreadNum;
        // Notify waiting threads that it's someone else's turn
        Monitor.Pulse(SyncRoot);
    }

    public static void  void Main()
    {
        Thread t1 = new Thread(new ThreadStart(() => Class1.thread1()));
        Thread t2 = new Thread(new ThreadStart(() => Class1.thread2()));
        t1.Start();
        t2.Start();
        t2.Join();
        t1.Join();
    }
}

As for using the lock keyword, it is not limited to synchronization within the same function. Lock "guarantees" exclusive access to a resource (object) to a single thread (by exclusive, I mean that only one thread can obtain a lock on that resource at a time, locking does not prevent other threads from simply accessing the object itself).

To simplify it, lock(someObject) is like a thread getting in line to use someOject and then waiting until all other threads in front of it have finished their turn before continuing. A thread ends its "turn" when it leaves the scope of the lock statement (unless you add things like Monitor.Pulse or Monitor.Wait).

like image 146
Jon Senchyna Avatar answered Dec 10 '22 11:12

Jon Senchyna


Use a ManualResetEvent. Wait for it in thread 2 and set it in thread1 after Console.WriteLn() http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx

like image 41
Kell Avatar answered Dec 10 '22 11:12

Kell