C# How run Threads with Async methods in command line app

Viewed 131

I simple do not know how to make the following code work, it is suppose to keep running forever inside an infinite loop, and in fact it will work when I remove await Task.Delay from the method Method_FOO, but I need Method_FOO to be async.

I think to make this work the Thread.Start() method needs to be "waitable" (not just the code it runs), but Thread.Start is void. I notice that if I block the execution with eg.: Console.ReadLine it will print the Worked string, but this is not a solution, and is terrible in real life.

This code is just an example, but the threads need to run inside an infinite loop (it is not something I can change), and I need async methods because I need to consume some websockets, and looks like there is no sync client websocket class in C#.

But still, there must be a simple/decent solution for this problem.

public static class Program
{
    public static async Task Main(string[] args)
    {
        var cancellationTokenSource = new CancellationTokenSource();
        var thread1 = new Thread(async () => await Run(cancellationTokenSource.Token, "threadName1", Method_FOO));
        var thread2 = new Thread(async () => await Run(cancellationTokenSource.Token, "threadName2", Method_FOO));

        thread1.Start();
        thread2.Start();
    }
    
    private static async Task Method_FOO(CancellationToken cancellationToken)
    {
        Console.WriteLine("It is called...");
        await Task.Delay(300, cancellationToken); 
        //never reach this part
        Console.WriteLine("Worked  ...");
    }

    // workd but it is not async
    //private static  Task Method_FOO(CancellationToken cancellationToken)
    //{
    //    Console.WriteLine("It is called...");
    //   Console.WriteLine("Worked  ...");
    //    return Task.CompletedTask;
    //}


    private static async Task Run(CancellationToken cancellationToken, string threadName, Func<CancellationToken, Task> function)
    {
        try
        {
            while (true)
            {
                await function(cancellationToken);
                Console.WriteLine($"{threadName} waiting ...");
                cancellationToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
    }
}
2 Answers

At this point in history, new Thread is pretty much only useful for COM interop. In every other scenario, there are better solutions. In this case, you can send work to the thread pool by using Task.Run:

public static async Task Main(string[] args)
{
  var cancellationTokenSource = new CancellationTokenSource();
  var task1 = Task.Run(async () => await Run(cancellationTokenSource.Token, "threadName1", Method_FOO));
  var task2 = Task.Run(async () => await Run(cancellationTokenSource.Token, "threadName2", Method_FOO));

  await Task.WhenAll(task1, task2);
}

Thread don't wait task's end. When the 3 threads (main, thread1, thread2), the program is closed and running task killed.

You can make the method Run synchronous and manualy wait the task end, like :

public static class Program
{
    public static async Task Main(string[] args)
    {
        var cancellationTokenSource = new CancellationTokenSource();
        var thread1 = new Thread(() => Run(cancellationTokenSource.Token, "threadName1", Method_FOO));
        var thread2 = new Thread(() => Run(cancellationTokenSource.Token, "threadName2", Method_FOO));

        thread1.Start();
        thread2.Start();
    }

    private static async Task Method_FOO(CancellationToken cancellationToken)
    {
        Console.WriteLine("It is called...");
        await Task.Delay(300, cancellationToken);

        Console.WriteLine("Worked  ...");
    }

    private static void Run(CancellationToken cancellationToken, string threadName, Func<CancellationToken, Task> function)
    {
        try
        {
            while (true)
            {
                function(cancellationToken).Wait();
                Console.WriteLine($"{threadName} waiting ...");
                cancellationToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
    }
}

Edit : Why not directly use the tasks?

public static void Main(string[] args)
{
    var cancellationTokenSource = new CancellationTokenSource();
    var task1 = Run(cancellationTokenSource.Token, "threadName1", Method_FOO);
    var task2 = Run(cancellationTokenSource.Token, "threadName2", Method_FOO);

    Task.Delay(TimeSpan.FromSeconds(5)).ContinueWith(t => cancellationTokenSource.Cancel());
    Task.WaitAll(task1, task2);
}
Related