使用 BlockingCollection 对任务进行排队
Using a BlockingCollection to queue Tasks
我正在尝试创建一种将任务排队到 运行 的方法,因此我尝试使用 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);
}
}
我觉得我在某处犯了一些小错误,因为感觉这应该可行。我试图找到做类似事情的人,但没有运气,这让我觉得我的概念可能有缺陷,所以请随时提出替代方案。
_processCollection.Add(BigTask(i));
不起作用,因为它会立即调用 BigTask(i)
,当它被调用时,工作就开始了。
通过将其包装在单独的 BigTask 启动器中,您走在了正确的轨道上,但是通过使用 Action
,您没有为 LaunchConsumer
提供任何跟踪进度的方法。 await Task.Run(processTask)
将立即继续下一个任务。您需要使用 Func<Task>
来避免这种情况。
您看不到任何结果的原因可能与此无关。现在您已设法从新创建的线程启动任务,对 textBox2.AppendText
的调用不再从 UI 线程完成。那是不支持的。只有 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
当您已经在后台线程中时,有时确实很有用,但不会在此处添加任何内容。该怎么做取决于您的需要。是否事先知道所有任务会有所不同。您是否需要多个消费者会有所不同。我没有想到的其他事情也可能有所不同。
我正在尝试创建一种将任务排队到 运行 的方法,因此我尝试使用 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);
}
}
我觉得我在某处犯了一些小错误,因为感觉这应该可行。我试图找到做类似事情的人,但没有运气,这让我觉得我的概念可能有缺陷,所以请随时提出替代方案。
_processCollection.Add(BigTask(i));
不起作用,因为它会立即调用 BigTask(i)
,当它被调用时,工作就开始了。
通过将其包装在单独的 BigTask 启动器中,您走在了正确的轨道上,但是通过使用 Action
,您没有为 LaunchConsumer
提供任何跟踪进度的方法。 await Task.Run(processTask)
将立即继续下一个任务。您需要使用 Func<Task>
来避免这种情况。
您看不到任何结果的原因可能与此无关。现在您已设法从新创建的线程启动任务,对 textBox2.AppendText
的调用不再从 UI 线程完成。那是不支持的。只有 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
当您已经在后台线程中时,有时确实很有用,但不会在此处添加任何内容。该怎么做取决于您的需要。是否事先知道所有任务会有所不同。您是否需要多个消费者会有所不同。我没有想到的其他事情也可能有所不同。