C# Signaling two threads with AutoResetEvent

1.9k views Asked by At


I need to create a program that counts to 10 using two threads.
one threads should print even numbers and the other should print odd numbers.
These threads should print the numbers in order (1, 2, 3, 4, 5...)

I have done this code but it seems not to work... any ideas?

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

namespace ConsoleApplication1
{
    class Program
    {
        private static AutoResetEvent autoEvent;
        private static int num = 10;

        static void DoWork(object data)
        {
            var counter = (int)data;

            if (counter == 1)
            {
                autoEvent.WaitOne();
            }

            while (counter <= num)
            {
                Console.WriteLine(counter);
                counter += 2;
                autoEvent.Set();
                autoEvent.WaitOne();
            }
        }

        static void Main(string[] args)
        {
            autoEvent = new AutoResetEvent(false);

            var evenCounter = new Thread(DoWork);
            evenCounter.Start(0);

            var notEvenCounter = new Thread(DoWork);
            notEvenCounter.Start(1);

            Console.ReadLine();
        }
    }
}
3

There are 3 answers

0
Alex On BEST ANSWER

In that code autoEvent.WaitOne() may not block because the AutoResetEvent is already in signaled state:

        autoEvent.Set();
        autoEvent.WaitOne();

So that thread will not wait for another thread as you planned.
To solve that problem you need to use 2 AutoResetEvents. One thread will wait on the first AutoResetEvent, second thread will wait on the second AutoResetEvent. Each thread will signal an AutoResetEvent that 'belongs' to another thread.
To demonstrate my idea I'll show you the lines of code only where you need to make a change:

private static AutoResetEvent autoEvents[] = new AutoResetEvent[2];

...

autoEvents[0] = new AutoResetEvent(false);
autoEvents[1] = new AutoResetEvent(false);

...

var counter = (int)data;
int initial_counter = counter;

...

if (counter == 1)
{
autoEvents[initial_counter].WaitOne();
}

...

autoEvents[1 - initial_counter].Set();
autoEvents[initial_counter].WaitOne();

At the end of DoWork signal the appropriate AutoResetEvent to avoid infinite blocking of another thread:

autoEvents[1 - initial_counter].Set();
1
thumbmunkeys On

Here seems to be your problem:

   autoEvent.Set();
   autoEvent.WaitOne();

You set the lock free and then immediately wait for the lock, which will lead to arbitrary execution patterns.

I suggest you use 2 locks, so one thread can block the other.

0
TsunamiCoder On
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadTest
{
    class Program
    {
        static void Main(string[] args)
        {

            Thread t1 = new Thread(Method1);
            Thread t2 = new Thread(Method2);
            t1.Start();
            Thread.Sleep(100);
            t2.Start();
            t1.Join();
            t2.Join();
            _autoEvent1.Close();
            _autoEvent2.Close();
        }

        private static void Method1()
        {

            do
            {
               _autoEvent2.WaitOne();

                if (!SharedCode(Thread.CurrentThread.ManagedThreadId))
                {
                    break;
                }

                _autoEvent1.Set();
            } while (true);
        }

        private static void Method2()
        {
            do
            {
                _autoEvent1.WaitOne();
                //_autoEvent2.Set();
                if (!SharedCode(Thread.CurrentThread.ManagedThreadId))
                    break;
                _autoEvent2.Set();
            } while (true);
        }

        private static bool SharedCode(int threadId)
        {
           lock (_lockObject)
            {

                Interlocked.Increment(ref _count);
                if (_count > 10)
                    return false;
                Console.WriteLine("ThreadId={0} , count={1}", threadId,_count);
            }

            return true;
        }
        private  static AutoResetEvent _autoEvent1 = new AutoResetEvent(true);
        private static AutoResetEvent _autoEvent2 = new AutoResetEvent(true);
        private static Object _lockObject = new object();
        private static int _count = 0;
    }
}