C# TPL数据流本地存储或类似的东西

C# TPL数据流本地存储或类似的东西,c#,task-parallel-library,tpl-dataflow,C#,Task Parallel Library,Tpl Dataflow,我试图实现的是,我有一个动作块MaxDegreeOfParallelism=4。我想为每个并行路径创建一个会话对象的本地实例,所以我想总共创建4个会话对象。如果这是线程,我将创建如下内容: ThreadLocal<Session> sessionPerThread = new ThreadLocal<Session>(() => new Session()); threadlocalsessionperthread=newthreadlocal(()=>news

我试图实现的是,我有一个动作块
MaxDegreeOfParallelism=4
。我想为每个并行路径创建一个会话对象的本地实例,所以我想总共创建4个会话对象。如果这是线程,我将创建如下内容:

 ThreadLocal<Session> sessionPerThread = new ThreadLocal<Session>(() => new Session());
threadlocalsessionperthread=newthreadlocal(()=>newsession());
我知道块不是线程,所以我在寻找类似的东西,但块。有没有办法创造这个

该区块处于服务状态,连续运行数月。在这段时间内,块的每个并发插槽使用了成吨的线程,因此线程本地存储是不合适的。我需要一些绑定到逻辑块插槽的东西。此外,此块永远不会完成,它将运行服务的整个生命周期


注:以上建议的答案对我的问题无效。我特别要求一些不同于线程本地的东西,上面的答案是使用线程本地。这是一个完全不同的问题。

听起来您已经知道,数据流块绝对不能保证块、执行和线程之间的相关性。即使将“最大并行度”设置为4,所有4个任务也可以在同一线程上执行。或者单个任务可以在多个线程上执行

考虑到您最终希望重用n个昂贵服务的实例来实现n个并行度,让我们暂时将数据流完全排除在外,因为它不会帮助(或直接阻碍)您获得此问题的任何通用解决方案。其实很简单。您可以使用
ConcurrentStack
,其中
T
是一种实例化成本很高的服务类型。您的代码显示在表示并行工作单元之一的方法(或委托)顶部:

private ConcurrentStack<T> reusableServices;

private void DoWork() {
    T service;
    if (!this.reusableServices.TryPop(out service)) {
        service = new T(); // expensive construction
    }

    // Use your shared service.
    //// Code here.

    // Put the service back when we're done with it so someone else can use it.
    this.reusableServices.Push(service);
}
私有ConcurrentStack可重用服务;
私房{
T服务;
if(!this.reusableServices.TryPop(out服务)){
service=new T();//昂贵的构造
}
//使用您的共享服务。
////代码在这里。
//完成服务后,请将其放回原处,以便其他人可以使用。
这个.reusableServices.Push(服务);
}
现在,通过这种方式,您可以很快看到,您创建的昂贵服务实例与并行执行
DoWork()
的实例数量完全相同。您甚至不必硬编码您期望的并行度。它与实际调度并行性的方式是正交的(所以线程池、数据流、PLINQ等都不重要)

因此,您只需使用DoWork()作为数据流块的委托,就可以开始了


当然,
ConcurrentStack
在这里没有什么神奇之处,除了push和pop周围的锁内置在类型中,因此您不必自己执行。

听起来您已经知道,数据流块绝对不能保证块、执行和线程之间的相关性。即使将“最大并行度”设置为4,所有4个任务也可以在同一线程上执行。或者单个任务可以在多个线程上执行

考虑到您最终希望重用n个昂贵服务的实例来实现n个并行度,让我们暂时将数据流完全排除在外,因为它不会帮助(或直接阻碍)您获得此问题的任何通用解决方案。其实很简单。您可以使用
ConcurrentStack
,其中
T
是一种实例化成本很高的服务类型。您的代码显示在表示并行工作单元之一的方法(或委托)顶部:

private ConcurrentStack<T> reusableServices;

private void DoWork() {
    T service;
    if (!this.reusableServices.TryPop(out service)) {
        service = new T(); // expensive construction
    }

    // Use your shared service.
    //// Code here.

    // Put the service back when we're done with it so someone else can use it.
    this.reusableServices.Push(service);
}
私有ConcurrentStack可重用服务;
私房{
T服务;
if(!this.reusableServices.TryPop(out服务)){
service=new T();//昂贵的构造
}
//使用您的共享服务。
////代码在这里。
//完成服务后,请将其放回原处,以便其他人可以使用。
这个.reusableServices.Push(服务);
}
现在,通过这种方式,您可以很快看到,您创建的昂贵服务实例与并行执行
DoWork()
的实例数量完全相同。您甚至不必硬编码您期望的并行度。它与实际调度并行性的方式是正交的(所以线程池、数据流、PLINQ等都不重要)

因此,您只需使用DoWork()作为数据流块的委托,就可以开始了


当然,
ConcurrentStack
在这里没有什么神奇之处,除了push和pop周围的锁内置在类型中,因此您不必自己执行。

可能重复No这不是TPL数据流线程本地数据的重复。他们正在询问如何使用线程本地数据,这是我明确不想使用的。我想要绑定到并发块插槽的东西,而不是线程。一个块中的任何给定插槽都可以在一个块的生命周期内跨越多个线程,这不是我想要的。除非这些数据流块没有“插槽”的概念。有最大的并行度,但这不一样,没有一个插槽可以在线程之间移动。您能解释一下为什么要这样做吗?我有一个会话对象,它的大小非常大,需要很长时间才能创建,几秒钟。我想同时使用其中的四个,但其中任何一个都不能同时使用,这不是线程安全的。我将在服务的生命周期内保留它们。可能的重复项否这不是TPL数据流线程本地数据的重复项。他们正在询问如何使用线程本地数据,这是我明确不想使用的。我想要绑定到并发块插槽的东西,而不是线程。块中的任何给定插槽都可以在b的生命周期内跨越多个线程