我有一个使用阻塞集合的简单生产者使用者设置。在我们的应用程序使用期间,使用者处于循环中,等待使用者将项目放入集合中,然后取出该项目并将其写入串行端口。由于某种原因collection.Take()会在集合中有项目时永远阻塞。对于此应用程序,我们可能一次激活一个或多个ProducerConsumers。无论如何,它们的行为都一样。
checkLoggedInF = user => {
this.checkLoggedInF$ && this.checkLoggedInF$.unsubscribe()
this.checkLoggedInF$ = interval(5000).pipe(
switchMap(() => this.usersService.isLoggedIn(user.loginnaam).pipe(
map(loggedIn => ({...user, loggedIn}))
))
);
return this.checkLoggedInF$;
}
运行此命令时,print语句会递增,因此我们肯定在Collection中有数据,但是无论出于何种原因,Take()都会继续阻塞。
它也不抛出异常。
Dispose()请求取消,但是我没有在这里添加。不会被称为早被叫。
我已经尝试过使用.GetConsumingEnumerable(),但它也会永远阻塞。
我启动任务错误吗?我会用完线程吗?
我已经考虑过使用BackgroundWorker代替Task,但是根据MSFT Task是首选。
谢谢。
答案 0 :(得分:1)
首先,我不会尝试创建自己的生产者/消费者实现,尤其是不会阻塞的实现。使用ActionBlock可以轻松处理简单的生产者/消费者方案。 ActionBlock具有一个内部队列,多个并发生产者可以向其发布消息。 ActionbBlock将使用传递给其构造函数的worker方法在后台处理排队的消息:
class SerialWorker
{
ActionBlock<Data> _serialBlock;
public SerialWorker()
{
_serialBlock=new ActionBlock<Data>(data=>DoWork(data));
}
//The worker action can be synchronous
private void DoWork(Data data)
{
}
//or asynchronous
private async Task DoWorkAsync(Data data)
{
}
//Producer Code
//While the application runs :
public void PostData(Data data)
{
_serialBlock.Post(someData);
}
//When the application finishes
//Tell the block to shut down and wait for it to process any leftover requests
public async Task Shutdown()
{
_serialBlock.Complete();
await _serialBlock.Completion;
}
worker方法可以是异步的,例如new ActionBlock<Data>(data=>DoWorkAsync(data))
可以正常工作。这样可以使用异步方法,而不会阻塞worker本身。
新消息以ActionBlock.Post
发布。当需要关闭时,应用程序应调用Complete()
来通知该动作块并等待其完成。终止之前,ActionBlock将停止接收更多消息并处理仍保留在缓冲区中的任何内容。