NeedToC
NeedToC

Reputation: 3

C# Multi-Threading - What's wrong, how to use AutoResetEvent

I'm still learning Threading, and I have problems with this code below. Sorry if this question appeared before, I just don't really understand why this code not working. I simplified the code:

    static EventWaitHandle waitH;      // AutoResetEvent, wait for signal
    static bool whExit;                // signal to exit waiting
    static Queue<string> str;          // waiting line (example values)
    static Queue<int> num;             // 

    static void Main(string[] args)
    {
        waitH = new AutoResetEvent(false);    // initialize waiter
        str = new Queue<string>();
        num = new Queue<int>();

        Thread thr = new Thread(new ThreadStart(Waiter));    // waiting in another thread
        thr.Start();                                         // start the waiting thread

        for(short i = 0; i < 10; i++)
        {
            str.Enqueue(string.Format($"{(char)(i + 65)}"));    // add something to queue
            num.Enqueue(i);    // add a number to test incrementing
            waitH.Set();    // signal to start the "long processing"
        }
    }

    static void Waiter()
    {
        while(!whExit)
        {
            waitH.WaitOne();    // wait for signal

            WriteToConsole();    // start the long processing on another thread
        }
    }

    static void WriteToConsole()
    {
        // threadstart with parameters
        // action: void delegate
        // get 2 values from waiting line
        var f = new ParameterizedThreadStart(obj =>
           new Action<string, int>(ConsoleWriter)
           (str.Dequeue(), num.Dequeue()));          // it's thread safe, because FIFO?

        Thread thr = new Thread(f);
        thr.IsBackground = true;           // close thread when finished
        thr.Start();
    }

    // print to console
    static void ConsoleWriter(string s, int n)
    {
        Console.WriteLine(string.Format($"{s}: {++n}"));     // easy example
    }

It stops in the Main's loop. I think the problem is: Thread.Start() called first, but it needs to change of state of Thread and joins the "need to be processed" queue , which takes time. The Main's loop already running and not wait for the signaling.

I solved this problem with Two-way signaling: used another pause-signal AutoResetEvent after waitH.Set() in the loop (WaitOne) and signal it after finished Console.WriteLine().

I'm not really proud of this solution, because if I do so, the program looses the "threadish", parallel, or synchronous approach. And this is an example, I would like to run long calculations at the same time, on different threads.

If I see the output, it's a book-fitting example that I'm doing it wrong:

Output: A: 1 B: 2 Sometimes B: 2 A: 1

Expected output: A: 1 B: 2 C: 3 D: 4 E: 5 F: 6 G: 7 H: 8 I: 9 J: 10

Is there any elegant way to solve this? Maybe to use locks, etc.

Any discussion would be appreciated. Thanks!

Upvotes: 0

Views: 859

Answers (1)

JonasH
JonasH

Reputation: 36371

There are several issues:

  1. The main method never waits for the worker thread to complete, so it will probably run to completion and stop all the threads before they are done. This could be solved by signaling the worker thread to stop, and then use thread.Join() to wait for it to complete.

  2. The WriteToConsole takes one item from each list and prints it to the console. But the thread might start after the loop in the main method has completed. So when the thread starts the autoReset event will be signaled and one item will be processsed. But in the next iteration the autoResetEvent will be unsignaled, and will never become signaled again. This can be solved by iterating over all items in the queue after the event has been signaled.

  3. Using two-way signaling in the loop like you mention will in effect serialize the code, removing any benefit of using threads.
  4. If this is a learning exercise i would suggest spending the time learning Tasks, async/await, lock and Parallel.For first. If you have a good grasp of these things you will be much more effective than using threads and reset events by hand.

// it's thread safe, because FIFO?

No. Use the concurrent collections if you want threadsafe collections.

Upvotes: 1

Related Questions