【问题标题】:C# How run Threads with Async methods in command line appC# 如何在命令行应用程序中使用异步方法运行线程
【发布时间】:2021-08-14 14:00:06
【问题描述】:

我只是不知道如何使下面的代码工作,假设在无限循环中永远运行,实际上当我从方法Method_FOO中删除await Task.Delay时它会工作,但我需要Method_FOO 异步。

我认为要使这项工作,Thread.Start() 方法需要是“可等待的”(不仅仅是它运行的代码),但 Thread.Start 是无效的。我注意到如果我用例如:Console.ReadLine 阻止执行,它将打印Worked 字符串,但这不是解决方案,在现实生活中很糟糕。

这段代码只是一个例子,但是线程需要在无限循环中运行(这不是我可以改变的),并且我需要异步方法,因为我需要消耗一些 websockets,并且看起来没有同步C# 中的客户端 websocket 类。

但是,对于这个问题,必须有一个简单/体面的解决方案。

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;
        }
    }
}

【问题讨论】:

    标签: c# multithreading lambda async-await


    【解决方案1】:

    在历史的这一点上,new Thread 几乎只对 COM 互操作有用。在其他所有情况下,都有更好的解决方案。在这种情况下,您可以使用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);
    }
    

    【讨论】:

    • 只是好奇,作为一个时不时使用 COM 互操作的人:需要 new Thread 而不是 Task.Run 的 COM 互操作用例是什么?
    • 你能解释一下将Run返回的任务封装在其他任务中的兴趣吗?
    • @Heinzi:如果您需要设置公寓状态并进行抽水,我建议使用手动线程。
    • @vernou:这种代码是一种“启动多个后台工作者”的架构。将每个线程都扔到线程池线程上是一种确保如果它们中的任何一个具有同步启动,那么这不会影响任何其他后台工作者。
    【解决方案2】:

    线程不等待任务结束。当 3 个线程(main、thread1、thread2)时,程序被关闭,正在运行的任务被杀死。

    您可以使方法Run同步并手动等待任务结束,例如:

    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;
            }
        }
    }
    

    编辑:为什么不直接使用任务?

    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);
    }
    

    【讨论】:

    • 这是一个很大的系统,到处都有线程,不幸的是它不是一个选项
    猜你喜欢
    • 1970-01-01
    • 2017-08-18
    • 2022-08-24
    • 2015-05-12
    • 1970-01-01
    • 2013-04-21
    • 2011-06-15
    • 1970-01-01
    • 2015-10-13
    相关资源
    最近更新 更多