我正在尝试创建一种将要运行的任务排队的方法,因此我尝试使用BlockingCollection
来实现它。我发现的问题是每当我尝试添加Task
时,任务就会执行。示例代码如下:
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");
}
调试中似乎发生的事情是,所有任务似乎都会在添加到阻塞集合中时运行。我尝试将阻止集合切换为使用Action
,但这只是导致没有发生任何事情。如下(仅显示更改):
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);
}
}
我觉得我在某个地方犯了一些小错误,因为感觉这应该有效。我试图找人做类似的事但没有运气,这让我觉得我的概念可能存在缺陷,所以可以随意提出替代方案。
答案 0 :(得分:2)
_processCollection.Add(BigTask(i));
不起作用,因为它会立即调用BigTask(i)
,当调用它时,工作就会开始。
通过将其包装在单独的BigTask启动器中,您处于正确的轨道上,但是使用Action
,您无法为LaunchConsumer
提供任何跟踪进度的方法。 await Task.Run(processTask)
将在下一个任务中立即继续。您需要使用Func<Task>
来避免这种情况。
您没有看到任何结果的原因可能不相关。既然您已设法从新创建的线程启动任务,则不再从UI线程调用textBox2.AppendText
。这不受支持。只有UI线程可以访问UI对象。您可以使用textBox2.Invoke
将操作传递回UI线程,然后该操作可以调用AppendText
。
经过测试的工作代码:
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")));
}
}
尽管如此,BlockingCollection
并不是真正最好的集合类型。它将一个线程专门用于等待。此外,Task.Run
当你已经在后台线程中时,有时可能会有用,但不会在这里添加任何内容。该怎么做取决于您的需求。是否事先知道所有任务都有所不同。您是否希望多个消费者有所作为。我没有想到的其他事情也可能有所不同。