c# – How do I stop a thread before starting a new instance of it?

Question:

Help to smooth out the flows. I will not provide the code, I just need an idea.

There is some event that triggers some thread. I need to make it so that every run of this thread terminates the execution of the previous instance.

Framework 4.0 (no async / await)

Answer:

The "old" and most clumsy way is to create a stream and kill it if necessary. However, this is potentially a very dangerous method, because, for example, a thread can leave shared data in an inconsistent state or refuse to terminate altogether.

class Program
{
    private static Thread _thread;

    static void Main(string[] args)
    {
        Console.ReadLine();
        TriggerEventOld();

        Console.ReadLine();
        TriggerEventOld();

        // дождемся завершения
        _thread.Join();
    }

    private static void TriggerEventOld()
    {
        if (_thread != null)
        {
            _thread.Abort();
            // корректнее будет дождаться завершения
            _thread.Join();
        }

        _thread = new Thread(Foo);
        _thread.Start();
    }

    private static void Foo()
    {
        Console.WriteLine("Foo started");

        try
        {
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(500); 
            }
        }
        catch (ThreadAbortException)
        {
            Console.WriteLine("Foo aborted");
        }

        Console.WriteLine("Foo ended");
    }
}

Better not to mess with Abort() , and use a "soft" thread termination using ManualResetEventSlim , which will serve as the termination flag. This method has one drawback – if you run synchronous code that can take a long time to respond (for example, reading a file or making a request to the network), you will have to wait a long time for the thread to complete when it is restarted.

class Program
{
    private static Thread _thread;
    private static ManualResetEventSlim _reset = new ManualResetEventSlim();

    static void Main(string[] args)
    {
        Console.ReadLine();
        TriggerEventOld();

        Console.ReadLine();
        TriggerEventOld();

        // дождемся завершения
        _thread.Join();
    }

    private static void TriggerEventOld()
    {
        if (_thread != null)
        {
            _reset.Set();
            // корректнее будет дождаться завершения
            _thread.Join();
        }

        _thread = new Thread(Foo);
        _reset.Reset();
        _thread.Start();
    }

    private static void Foo()
    {
        Console.WriteLine("Foo started");

        for (int i = 0; i < 10; i++)
        {
            if (_reset.IsSet)
            {
                Console.WriteLine("Foo canceled");
                break;
            }

            Thread.Sleep(500); 
        }

        if (!_reset.IsSet)
        {
            Console.WriteLine("Foo ended");
        }
    }
}

But since you are using .NET 4.0, the TPL is available to you. In this case, it would be better to use Task and CancellationToken , with them you can make a "soft" stop of the task. The advantage of the new model is that almost all asynchronous methods support stopping with a CancellationToken , so your code will respond to completion faster.

class Program
{
    private static Task _task;
    private static CancellationTokenSource _cts;

    static void Main(string[] args)
    {
        Console.ReadLine();
        TriggerEventNew();

        Console.ReadLine();
        TriggerEventNew();

        // дождемся завершения;
        // этот вызов может выбросить исключение,
        // если внутри возникнет необработанное исключение
        _task.Wait();
    }

    private static void TriggerEventNew()
    {
        if (_task != null)
        {
            _cts.Cancel();
            // корректнее будет дождаться завершения;
            // этот вызов может выбросить исключение,
            // если внутри возникнет необработанное исключение
            _task.Wait();
        }

        // пересоздаем каждый раз, поскольку это одноразовый объект
        _cts = new CancellationTokenSource();
        _task = Task.Factory.StartNew(() => FooNew(_cts.Token), _cts.Token);
    }

    private static void FooNew(CancellationToken token)
    {
        Console.WriteLine("FooNew started");

        for (int i = 0; i < 10; i++)
        {
            if (token.IsCancellationRequested)
            {
                Console.WriteLine("FooNew aborted");
                break;
            }

            Thread.Sleep(500);
        }

        if (!token.IsCancellationRequested)
        {
            Console.WriteLine("FooNew ended");
        }
    }
}

If you use token.ThrowIfCancellationRequested() , do not forget to wrap the code in a try/catch with token validation:

try
{
    ...
    token.ThrowIfCancellationRequested();
    ...
}
catch (OperationCanceledException e)
{
    if (e.CancellationToken != token)
    {
        // отмена была вызвана не нами, бросаем исключение
        throw;
    }
}
Scroll to Top