0

Experts,

In C# I need to do some endless looping work beside the main (GUI) thread. So, the main thread creates the looping thread and when the main thread reaches a certain point, it should end that loop. Here my code which I know is wrong:

private bool m_MayRun;
private Thread m_Thread;

void mainthread() //e.g. the Form_Load event
{
    ...
    m_Thread = new Thread(loopingThread);
    m_Thread.Start();
    ...
    m_MayRun = false; // e.g. later in the Form_Closing event
    m_Thread.Join();
}

void loopingThread()
{
    m_MayRun = true;
    while(m_MayRun) // loop until m_MayRun is set to 'false' from the main thread
    {
        DoStuff();
    }
}

The obious wrong bit is the use of the variable 'm_MayRun' which leads to sometimes block the mainthread, especially the 'join' command. In C++ I would declare it atomic but I couldn't find such a thing in C#. This m_MayRun varible is the only one that has to be accessed from the main thread, so I am looking for a simple and beautiful way (without a big overhead) to solve this synchronization issue. Thanks

M. Enke
  • 41
  • 6
  • 2
    What about passing a [`CancellationToken`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken?view=netcore-3.1) to your `loopingThread` method, and make it check on every loop if `IsCancellationRequested` is true, and if so `break` from the loop. Now you can privately store the `CancellationTokenSource` and call `Cancel` whenever needed ([ms docs on cancelling managed threads](https://learn.microsoft.com/en-us/dotnet/standard/threading/cancellation-in-managed-threads)) – MindSwipe May 20 '20 at 10:59
  • I think that the main thread sometimes is blocked because of the method `thread.Join()` and job that is executed inside `while loop DoStuff()`. Do you really need to wait until background thread ends (for example, after its completion you use the result of its calculations)? Or do you just want to finish background thread and there is no need to wait until it finishes? – Iliar Turdushev May 20 '20 at 11:23
  • I think I wouldn't overengineer here. Just make `m_MayRun` volatile and do not set it `true` _inside_ the thread, but before you start. Though not particularly probable, it may happen that the thread is not actually started before the main thread reaches the `Join`. In that case you'd have an endlessly running Thread and a blocked Event Queue. – Fildor May 20 '20 at 11:48
  • Do you really `DoStuff` non-stop in the loop? There is no delay between one `DoStuff` and the next? – Theodor Zoulias May 20 '20 at 13:20
  • @Iliar Turdeshev: I just need to cancel the endles loop in a proper way for cleaning up. I know the 'abort' command but this means I have to use a try/catch/finally construction in the thread which I do not like for several reasons. – M. Enke May 20 '20 at 13:44
  • I ask because I'm insterested if it's applicable for you to remove call to `thread.Join()` and let background thread to finish without waiting for it. If you wait for it then there is always a chance of the main thread to block. And choosing different approach to synchronize access to the variable `m_MayRun` cannot stop main thread from being blocking sometimes. – Iliar Turdushev May 21 '20 at 04:25

2 Answers2

1

Using the volatile keyword as others suggested is a good start. The volatile keyword basically tells the compiler that the variable will be accessed from different threads which will lead the compiler to reduce/disable caching of said variable. Threads may run on different cores/processors that use different caches which store different versions of your variable.

If you want to make sure that the threads are not accessing the variable simultaneously you can use a lock. A lock will cause your thread to wait until the lock is free. Typically you create an object inside your class that is reserved just for that purpose. With locks and the volatile keyword your code would look like this:

private volatile bool m_MayRun;
private Thread m_Thread;
private readonly object _lock = new object();

void mainthread() //e.g. the Form_Load event
{
    ...
    m_Thread = new Thread(loopingThread);
    m_Thread.Start();
    ...

    lock(_lock)
        m_MayRun = false; // e.g. later in the Form_Closing event

    m_Thread.Join();
}

void loopingThread()
{
    lock(_lock)
        m_MayRun = true;

    while(true) 
    {
        lock(_lock)
        {
            if(m_maxRun)
                break;
        }
        DoStuff();            
    }
}

Edit: As @Theodor Zoulias pointed out, you don't have to use the volatile keyword with locking. Actually you can use volatile OR locks OR interlocked in this specific case. To get an overview of each methods benefits you might want to take a look at Volatile vs. Interlocked vs. lock

somebody
  • 89
  • 1
  • 4
0

The lowest overhead method is probably to use methods in the interlocked class. A more typical c# way would be to create a task with a Cancellation token. A third option would be manualResetEvent(Slim). There is also the volatile -keyword that seem like it should solve synchronization in this case.

JonasH
  • 28,608
  • 2
  • 10
  • 23