【发布时间】:2015-08-29 15:28:46
【问题描述】:
调用_thread.Join() 会导致GetConsumingEnumerable 循环卡在最后一个元素上。为什么会出现这种行为?
public abstract class ActorBase : IDisposable
{
private readonly BlockingCollection<Task> _queue = new BlockingCollection<Task>(new ConcurrentQueue<Task>());
private readonly Thread _thread;
private bool _isDisposed;
protected ActorBase()
{
_thread = new Thread(ProcessMessages);
_thread.Start();
}
protected void QueueTask(Task task)
{
if (_isDisposed)
{
throw new Exception("Actor was disposed, cannot queue task.");
}
_queue.Add(task);
}
private void ProcessMessages()
{
foreach (var task in _queue.GetConsumingEnumerable())
{
task.RunSynchronously();
}
}
public void Dispose()
{
_isDisposed = true;
_queue.CompleteAdding();
_thread.Join();
}
}
public class SampleActor : ActorBase
{
private string GetThreadStatus()
{
Thread.Sleep(500);
return string.Format("Running on thread {0}", Thread.CurrentThread.ManagedThreadId);
}
public async Task<string> GetThreadStatusAsync()
{
var task = new Task<string>(GetThreadStatus);
QueueTask(task);
return await task;
}
}
class Program
{
public static async Task Run()
{
using (var sa = new SampleActor())
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine(await sa.GetThreadStatusAsync());
}
}
}
public static void Main(string[] args)
{
Console.WriteLine("Main thread id {0}", Thread.CurrentThread.ManagedThreadId);
var task = Task.Run(async ()=> { await Run(); });
task.Wait();
}
}
这种方法的背景是我需要确保所有操作都在一个 OS 线程上执行,这将允许应用程序的一部分使用与主线程不同的凭据。
【问题讨论】:
-
我无法向您解释为什么会发生这种情况,但它锁定的原因是
GetThreadStatusAsync()中的return await task;在调用RunSynchronously()的同一线程上完成了它的等待。当GetThreadStatusAsync()返回并退出Run中的using块时,这会导致Dispose从保存的_thread线程内部运行,现在您正在从要加入的线程内部调用Join(),僵局!
标签: c# multithreading async-await task-parallel-library threadpool