0

I made a little example program that counts numbers using two threads. It also prints a second number next to the count so I can see which thread printed which number.

Now my goal is to make both threads immediately stop once one of them counted 7. I don't know how to go about it. I thought about passing a thread array as a parameter to Counter and then use a foreach loop to abort both threads. The problem is that t0 might be executing it and calls t0.Abort()and thus t1.Abort() won't be called anymore.

public static int count = 0;
private static object lockObject = new object();

static void Main(string[] args) {
    Thread t0 = new Thread(() => Counter(1, 10));
    Thread t1 = new Thread(() => Counter(10, 20));

    t0.Start();
    t1.Start();

    t0.Join();
    t1.Join();

    Console.ReadLine();
}

public static void Counter(int k, int m) {
    for(int i = k; i < m; i++) {
        lock (lockObject) {
            count++;
            Console.WriteLine(count + " " + i);
            if (i == 7) {
                /* 
                 * Code that will kill thread t0 and t1
                 */
            }
        }
    }
}

The output should be something like

1 1
2 2
3 10
4 11
5 12
6 13
7 14

Does someone have suggestions how to kill both t0 and t1?

  • Would you be happy using `Task`s instead of plain Threads? If so there's a simple way to wait until the first one finishes in the main thread, then you can terminate all of them. – Dylan Nicholson Dec 01 '17 at 21:51
  • Your goal is that neither thread should print a number greater than 14, right? And the count never decreases, right? Why not just have each thread kill _itself_ (i.e., return) when it sees count > 14? – Solomon Slow Dec 01 '17 at 21:52
  • `CancellationTokenSource` is your friend. – code4life Dec 01 '17 at 21:53
  • No, my goal is that the threads stop immediately when it counts 7. The second number is just to see from which thread it came. –  Dec 01 '17 at 21:55

3 Answers3

2

Use a flag, in this case "run".

public static int count = 0;
private static object lockObject = new object();
static bool run = true;

static void Main(string[] args) {
    Thread t0 = new Thread(() => Counter(1, 10));
    Thread t1 = new Thread(() => Counter(10, 20));        

    t0.Start();
    t1.Start();

    t0.Join();
    t1.Join();

    Console.ReadLine();
}

public static void Counter(int k, int m) {
    for(int i = k; i < m && run; i++) {
        lock (lockObject) {
            count++;
            Console.WriteLine(count + " " + i);
            if (i == 7) {
                run = false;
            }
        }
    }
}
Evk
  • 98,527
  • 8
  • 141
  • 191
epinal
  • 1,415
  • 1
  • 13
  • 27
  • 1
    @Robbebeest It's *extremely* hard to use correctly, and you need to be extremely knowledgeable about how it works and *exactly* what it does if you want to use it. If you're not, you should use proper synchronization, like a `lock`, instead, as is done here, instead of trying to use `volatile`. – Servy Dec 01 '17 at 22:12
  • @Evk Exactly right :-). If you got a chance read this, its a pretty good explanation: https://stackoverflow.com/questions/72275/when-should-the-volatile-keyword-be-used-in-c – epinal Dec 01 '17 at 22:14
  • This still looks wrong to me. `run` could be `true` to enter the loop and get held up at the `lock` while the other thread is running and has the lock. That other thread would then set `run` to `false`, but too late! The other thread has already passed that check. To correct the issue, the flag check should be inside the lock. – itsme86 Dec 01 '17 at 22:42
1

Have a look at ManualResetEvent. You should create one and inside the for-loop of the Counter-method you should check if the event is set and if so, break the for-loop and return. If you reach 7, simply set the event. The event is thread-safe.

By the way, seems to be a university homework ;) next time, try it yourself.

public static int count = 0;
private static object lockObject = new object();
private ManualResetEvent finish = new ManualResetEvent(false);

static void Main(string[] args) {
    Thread t0 = new Thread(() => Counter(1, 10));
    Thread t1 = new Thread(() => Counter(10, 20));

    t0.Start();
    t1.Start();

    t0.Join();
    t1.Join();

    Console.ReadLine();
}

public static void Counter(int k, int m) {
    for(int i = k; i < m; i++) {

        lock (lockObject) {
            if (finish.waitOne(0))
                break;

            count++;
            Console.WriteLine(count + " " + i);
            if (i == 7)
                finish.set()
        }
    }
}
Matthias
  • 5,574
  • 8
  • 61
  • 121
  • It still counts past 7. I tried some more things on my own but sometimes you don't even know what to search for. I had for example never heard of ManualResetEvent. But yeah, it's a small part of a big exercise ;) –  Dec 01 '17 at 22:03
  • Ah, then move the if (finish.isset()) just right after the lockObject. It still counts past 7 because one thread is waiting for the lockObject and not looking at the event anymore (because the event is before the lock). I updated the code. – Matthias Dec 01 '17 at 22:05
  • You can also use a semaphore or the event.waitOne method for synchonization instead of the lockObject. – Matthias Dec 01 '17 at 22:08
0

MSDN Doesn't know what is ManualResetEvent.isset(), but Matthias's answer looks correct. If replace isset() to WaitOne(0), code works fine.

private static int count = 0;
private static ManualResetEvent finish = new ManualResetEvent(false);
private static int stopValue = 7;
private static int syncInterval = 0; //0ms
private static object lockObject = new object();
static void Main(string[] args)
{
    Thread t0 = new Thread(() => Counter(1, 10));
    Thread t1 = new Thread(() => Counter(10, 20));
    t0.Name = "Thread 1";
    t1.Name = "Thread 2";

    t0.Start();
    t1.Start();

    t0.Join();
    t1.Join();

    Console.ReadKey();
}
public static void Counter(int k, int m)
{
    for (int i = k; i < m; i++)
    {
        lock (lockObject)
        {
            if (finish.WaitOne(syncInterval))
                break;
            count++;
            Console.WriteLine($"{count} {i}");
            if (count == stopValue)
            {
                finish.Set();
            }
        }
    }
}
  • You’re right. I’m not working with C# anymore and had no visual studio installed. So the code was just out of my brain – Matthias Dec 03 '17 at 19:43