【问题标题】:C# .Is there a .NET class for custom thread pools [closed]C#。是否有用于自定义线程池的 .NET 类 [关闭]
【发布时间】:2017-04-07 23:25:48
【问题描述】:

我一直在努力寻找一个简单的自定义线程池,但找不到,所以写了一个快速穷人的线程池。

问题

  1. 是否有任何 .NET 类可以做到这一点? (经过多次尝试都找不到它,只有博客文章上的自定义实现比这复杂得多!)。

  2. 当任务进入时,最好通过轮询或启动线程池上的操作来消耗线程?

代码

public class WorkerQueue : IWorkerQueue
{
    private readonly Queue<WorkItem> _items = new Queue<WorkItem>();

    private int _max = 2; // Would be configurable
    private int _running;
    private Stopwatch _stopwatch;

    public WorkerQueue()
    {
        _stopwatch = new Stopwatch();
        _stopwatch.Start();
    }

    public void Add(WorkItem workItem)
    {
        lock (_items)
        {
            if (_running >= _max)
            {
                Log($"Queuing Item {workItem.Name} - _running >= _max");
                _items.Enqueue(workItem);
                return;
            }

            _running++;

            Log($"Running Item {workItem.Name} - _running = {_running}");
            var task = Task.Run(workItem.Action);

            task.ContinueWith(t => OnActionCompleted(workItem.Name));
        }
    }

    private void Log(string msg)
    {
        Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} @ {_stopwatch.ElapsedMilliseconds}ms : {msg}");
    }

    private void OnActionCompleted(string obj)
    {
        Log($"OnActionCompleted {obj}");
        WorkItem item = null;

        lock (_items)
        {
            if (_items.Count > 0)
                item = _items.Dequeue();
            else
                _running--;
        }

        if (item != null)
        {
            // Potential Stack Overflow if big queue builds up?
            // Probably should be a while loop rather than recursion?
            Log($"Running Next Item {item.Name}");
            item.Action();
            OnActionCompleted(item.Name);
        }
        else
        {
            Log($"Sleeping. _running = {_running}");
        }
    }
}

还有一个测试:

    [Fact]
    public void Test()
    {
        var sb = new StringBuilder();
        Console.SetOut(new StringWriter(sb));

        var resetEvent = new ManualResetEventSlim();

        AddItem("A", 100);
        AddItem("B", 250);
        AddItem("C", 100);
        AddItem("D", 100);
        AddItem("E", 100);
        AddItem("G", 100, () =>
        {
            Thread.Sleep(250);
            resetEvent.Set();
        });

        resetEvent.Wait(2500);

        Assert.True(resetEvent.IsSet);

        _output.WriteLine("");
        _output.WriteLine("------------------ Test Finished ------------------");
        _output.WriteLine("------------------  Console Out  ------------------");
        _output.WriteLine("");
        _output.WriteLine(sb.ToString());
    }

并得到正确(或足够正确)的输出

Thread 14 @ 8ms : Running Item A - _running = 1
Thread 14 @ 8ms : Running Item B - _running = 2
Thread 14 @ 8ms : Queuing Item C - _running >= _max
Thread 14 @ 8ms : Queuing Item D - _running >= _max
Thread 14 @ 8ms : Queuing Item E - _running >= _max
Thread 14 @ 8ms : Queuing Item G - _running >= _max
Thread 21 @ 110ms : OnActionCompleted A
Thread 21 @ 110ms : Running Next Item C
Thread 21 @ 211ms : OnActionCompleted C
Thread 21 @ 211ms : Running Next Item D
Thread 20 @ 260ms : OnActionCompleted B
Thread 20 @ 260ms : Running Next Item E
Thread 21 @ 311ms : OnActionCompleted D
Thread 21 @ 311ms : Running Next Item G
Thread 20 @ 360ms : OnActionCompleted E
Thread 20 @ 360ms : Sleeping. _running = 1
Thread 21 @ 662ms : OnActionCompleted G
Thread 21 @ 662ms : Sleeping. _running = 0

【问题讨论】:

  • 我投票结束这个问题,因为它应该属于 codereview.stackexchange.com 站点
  • @SergeyBerezovskiy 将问题改为询问 .NET 框架中是否已有可用的东西
  • Task 是 .NETCore 中的新线程,它通过线程池进行管理。

标签: c# multithreading threadpool


【解决方案1】:

你可以看看parallel extensions extras project,它有几个任务调度器。具体来说,WorkStealingTaskScheduler

我记得曾经测试过它,托管世界中没有任何东西可以与 .NET 自己的线程池相比,至少没有不安全的代码。 .NET 的库存线程池在底层有很多优化,我清楚地记得其中一个是在等待之前处理一个工作项之后积极地为工作项旋转。

从好的方面来说,您可以非常接近它,但模仿 ThreadPool 所做的事情是一项壮举。也就是说,如果你想这样做。我希望 使用默认线程池的原因之一是它关于在 min 之后缓慢创建线程的策略。线程,但只是增加最小值。线程通常足够好。

问题是,除非您使用 TPL并且,否则您可以提供自己的任务调度程序,通常没有其他东西,尤其是遗留代码,将获取您的自定义线程池,因为没有库存线程池接口。不幸的是,ThreadPool 是一个静态类这一事实进一步阻碍了其他线程池的使用。

【讨论】:

  • 我不是在寻找覆盖默认线程池,而是一个单独的线程池,我可以对其工作,并指定一次可以运行多少个。
  • 我明白了,你读过我的第一段吗? WorkStealingTaskScheduler 可能是最接近微软自己在托管代码中实现的线程池的东西。但请参阅同一项目中的LimitedConcurrencyLevelTaskScheduler
猜你喜欢
  • 1970-01-01
  • 2011-04-28
  • 2010-11-05
  • 2017-08-06
  • 2011-07-06
  • 1970-01-01
  • 2012-06-01
  • 1970-01-01
  • 2013-06-15
相关资源
最近更新 更多