Using a BlockingCollection to queue Tasks

时光总嘲笑我的痴心妄想 提交于 2019-12-11 00:53:16

问题


I am trying to create a way to queue up Tasks to run, so I have tried to implement it using a BlockingCollection. The problem I find is whenever I try to add the Task, the Task executes. Sample code as below:

private void button1_Click(object sender, EventArgs e)
{
    textBox2.Clear();
    for (int i = 0; i < 10; i++)
    _processCollection.Add(BigTask(i));
}

static BlockingCollection<Task> _processCollection = new BlockingCollection<Task>();
Thread ConsumerThread = new Thread(LaunchConsumer);

private static async void LaunchConsumer()
{
    while (true)
    {
        var processTask = _processCollection.Take();
        await Task.Run(() => processTask);
    }
}

async Task BigTask(int i)
{
    await Task.Delay(5000);
    textBox2.AppendText($"Text{i}\n");
}

What seems to happen in debug is all the tasks seem to run as they are added into the blocking collection. I tried switching the blocking collection to use Action, but that just leads to nothing happening. As below (only changes shown):

private void button1_Click(object sender, EventArgs e)
{
    textBox2.Clear();
    for (int i = 0; i < 10; i++)
    {
        int iC = i;
        _processCollection.Add(async () => await BigTask(iC));
    }
}

static BlockingCollection<Action> _processCollection = new BlockingCollection<Action>();
Thread ConsumerThread = new Thread(LaunchConsumer);

private static async void LaunchConsumer()
{
    while (true)
    {
        var processTask = _processCollection.Take();
        await Task.Run(processTask);
    }
}

I feel like I have made some small error somewhere, because it feels like this should work. I have tried to find someone doing something similar but have had no luck, which makes me think maybe my concept is flawed so feel free to suggest an alternative.


回答1:


_processCollection.Add(BigTask(i)); doesn't work because this calls BigTask(i) immediately, and when that is called, the work starts.

You were on the right track by wrapping this in a separate BigTask launcher, but by using Action, you don't provide your LaunchConsumer with any means to track the progress. await Task.Run(processTask) will continue pretty much immediately with the next task. You need to use Func<Task> to avoid that.

The reason you don't see any results is likely unrelated. Now that you manage to launch the task from your newly created thread, the call to textBox2.AppendText is no longer done from the UI thread. That's not supported. Only the UI thread can access UI objects. You can use textBox2.Invoke to pass an action back to the UI thread, and that action can then call AppendText.

Tested working code:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        ConsumerThread.Start();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        textBox2.Clear();
        foreach (var i in Enumerable.Range(0, 10))
            _processCollection.Add(() => BigTask(i));
    }

    static BlockingCollection<Func<Task>> _processCollection = new BlockingCollection<Func<Task>>();
    Thread ConsumerThread = new Thread(LaunchConsumer);

    private static async void LaunchConsumer()
    {
        while (true)
        {
            var processTask = _processCollection.Take();
            await Task.Run(processTask);
        }
    }

    async Task BigTask(int i)
    {
        await Task.Delay(5000);
        textBox2.Invoke(new Action(() => textBox2.AppendText($"Text{i}\n")));
    }
}

That said, BlockingCollection is not really the best collection type to use here. It dedicates one thread to pretty much nothing but waiting. Also, Task.Run when you're already in a background thread can admittedly sometimes be useful, but doesn't add anything here. What to do instead depends on your needs. Whether all tasks are known beforehand makes a difference. Whether you may want multiple consumers makes a difference. Other things I haven't thought of may also make a difference.



来源:https://stackoverflow.com/questions/36761955/using-a-blockingcollection-to-queue-tasks

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!