首页 > 解决方案 > 如何在 C# 中运行可变数量的并发参数化无限循环类型的线程?

问题描述

我正在创建我的第一个基于多线程 C#/.NET 的应用程序,它将在 Azure Service Fabric 集群上运行。正如标题所说,我希望运行可变数量的并发参数化无限循环类型的线程,这将利用 RunAsync 方法。

每个子线程看起来像这样:

        public async Task childThreadCall(...argument list...)
        {
            while (true)
            {
                try
                {
                    //long running code
                    //do something useful here
                    //sleep for an independently parameterizable period, then wake up and repeat
                }
                catch (Exception e)
                {
                    //Exception Handling
                }
            }
        }

在 RunAsync 方法中调用的此类子线程数量不定。我想做这样的事情:

        protected override async Task RunAsync(CancellationToken cancellationToken)
        {
            try
            {
                for (int i = 0; i < input.length; i++)
                {
                    ThreadStart ts[i] = new ThreadStart(childThreadCall(...argument list...));
                    Thread tc[i] = new Thread(ts);
                    tc[i].Start();
                }
            }
            catch (Exception e)
            {
                //Exception Handling
            }
        }

所以基本上每个子线程都独立于其他线程运行,并且永远这样做。有可能做这样的事情吗?有人能指出我正确的方向吗?有什么陷阱需要注意吗?

标签: c#.netmultithreadingazureazure-service-fabric

解决方案


RunAsync方法在服务启动时被调用。所以是的,它可以用来做你想做的事。我建议使用任务,因为它们可以很好地与取消令牌配合使用。这是一个粗略的草稿:

protected override async Task RunAsync(CancellationToken cancellationToken)
{
    var tasks = new List<Task>();
    try
    {
        for (int i = 0; i < input.length; i++)
        {
            tasks.Add(MyTask(cancellationToken, i);
        }
        
        await Task.WhenAll(tasks);
    }
    catch (Exception e)
    {
        //Exception Handling
    }
}

public async Task MyTask(CancellationToken cancellationToken, int a)
{
    while (true)
    {
        cancellationToken.ThrowIfCancellationRequested();

        try
        {
            //long running code, if possible check for cancellation using the token
            //do something useful here
            await SomeUseFullTask(cancellationToken);
            
            //sleep for an independently parameterizable period, then wake up and repeat
            await Task.Delay(TimeSpan.FromHours(1), cancellationToken);
        }
        catch (Exception e)
        {
            //Exception Handling
        }
    }
}

关于陷阱,有一个很好的列表,列出了在使用任务时一般要考虑的事情。

请注意,任务最适合 I/O 绑定工作。如果您可以发布在长期运行过程中究竟做了什么,那么我可以改进答案以最适合您的用例。

一件重要的事情是尊重传递给 RunAsync 方法的取消令牌,因为它表明服务即将停止。它让您有机会优雅地停止工作。从文档

确保传递给 RunAsync(CancellationToken) 的 cancelToken 得到兑现,一旦发出信号, RunAsync(CancellationToken) 就会尽快正常退出。请注意,如果 RunAsync(CancellationToken) 已经完成了它的预期工作,它不需要等待 cancelToken 发出信号并且可以优雅地返回。

正如您在我的代码中看到的那样,我将传递CancellationToken给子方法,以便他们可以对可能的取消做出反应。在您的情况下,由于无限循环,将会取消。


推荐阅读