[英]TPL Dataflow Blocks
问题:为什么使用WriteOnceBlock
(或BufferBlock
)从另一个BufferBlock<Action>
取回答案(如某种回调)(获取答案在发布的Action
发生),会导致死锁(在此代码中)?
我认为类中的方法可以视为我们正在发送给对象的消息(例如,我认为-Alan Kay提出的有关OOP的原始观点)。 因此,我编写了这个通用的Actor
类,该类有助于将普通对象转换为Actor
(当然,由于可变性和其他原因,这里存在很多看不见的漏洞,但这并不是这里要关注的主要问题)。
因此,我们有以下定义:
public class Actor<T>
{
private readonly T _processor;
private readonly BufferBlock<Action<T>> _messageBox = new BufferBlock<Action<T>>();
public Actor(T processor)
{
_processor = processor;
Run();
}
public event Action<T> Send
{
add { _messageBox.Post(value); }
remove { }
}
private async void Run()
{
while (true)
{
var action = await _messageBox.ReceiveAsync();
action(_processor);
}
}
}
public interface IIdGenerator
{
long Next();
}
现在; 该代码为何起作用:
static void Main(string[] args)
{
var idGenerator1 = new IdInt64();
var idServer1 = new Actor<IIdGenerator>(idGenerator1);
const int n = 1000;
for (var i = 0; i < n; i++)
{
var t = new Task(() =>
{
var answer = new WriteOnceBlock<long>(null);
Action<IIdGenerator> action = x =>
{
var buffer = x.Next();
answer.Post(buffer);
};
idServer1.Send += action;
Trace.WriteLine(answer.Receive());
}, TaskCreationOptions.LongRunning); // Runs on a separate new thread
t.Start();
}
Console.WriteLine("press any key you like! :)");
Console.ReadKey();
Trace.Flush();
}
此代码不起作用:
static void Main(string[] args)
{
var idGenerator1 = new IdInt64();
var idServer1 = new Actor<IIdGenerator>(idGenerator1);
const int n = 1000;
for (var i = 0; i < n; i++)
{
var t = new Task(() =>
{
var answer = new WriteOnceBlock<long>(null);
Action<IIdGenerator> action = x =>
{
var buffer = x.Next();
answer.Post(buffer);
};
idServer1.Send += action;
Trace.WriteLine(answer.Receive());
}, TaskCreationOptions.PreferFairness); // Runs and is managed by Task Scheduler
t.Start();
}
Console.WriteLine("press any key you like! :)");
Console.ReadKey();
Trace.Flush();
}
此处用于创建Task
的不同TaskCreationOptions
。 也许我对这里的TPL Dataflow概念是错误的,才刚刚开始使用它(一个[ThreadStatic]
隐藏在某个地方?)。
您的代码有问题的部分是: answer.Receive()
。 在动作中移动死锁时,不会发生死锁:
var t = new Task(() =>
{
var answer = new WriteOnceBlock<long>(null);
Action<IIdGenerator> action = x =>
{
var buffer = x.Next();
answer.Post(buffer);
Trace.WriteLine(answer.Receive());
};
idServer1.Send += action;
});
t.Start();
那为什么呢? answer.Receive();
,而不是await answer.ReceiveAsnyc();
阻塞线程,直到返回答案。 当您使用TaskCreationOptions.LongRunning
每个任务都有自己的线程,因此没有问题,但是没有它( TaskCreationOptions.PreferFairness
无关紧要),所有线程池线程都在忙于等待,因此一切都慢得多。 它实际上并没有死锁,如使用15而不是1000所见。
还有其他解决方案可帮助您理解问题:
ThreadPool.SetMinThreads(1000, 0);
增加线程池ThreadPool.SetMinThreads(1000, 0);
原始代码之前。 ReceiveAsnyc
: Task.Run(async () =>
{
var answer = new WriteOnceBlock<long>(null);
Action<IIdGenerator> action = x =>
{
var buffer = x.Next();
answer.Post(buffer);
};
idServer1.Send += action;
Trace.WriteLine(await answer.ReceiveAsync());
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.