C#: 在ASP.NET中为Parallel.ForEach()添加上下文

13

我有一个静态类,其中包含一个静态的get属性,在此属性中,我执行以下操作:

// property body
{
    // HttpContext.Current is NOT null
    ...

    Parallel.ForEach(files, file =>
    {
        // HttpContext.Current is null
        var promo = new Promotion();
        ...
    });
    ...

    // HttpContext.Current is NOT null
}

这个静态类在视图使用了该属性之前并不会进行类型初始化。

问题在于,Promotion 的静态构造函数会在 Parallel.ForEach() 中第一次创建 new Promotion() 时被初始化,并使用 HttpContext.Current。 在此 Parallel.ForEach() 范围内实例化 promo 时,HttpContext.Currentnull,因此 new Promotion() 会引发异常。

HttpContext.Current 在静态获取属性中不为 null,因为它在视图使用它之前不会被调用(因此有一个HttpContext.Current)。

如果 Promotion 在其实例中使用 HttpContext.Current 而不是其静态成员,则可能可以将 HttpContext.Current 传递到 new Promotion() 构造函数中:

 var context = HttpContext.Current;
 Parallel.ForEach(files, file =>
 {
     var promo = new Promotion(context);
 });

但是由于Promotion类的静态成员需要HttpContext.Current,所以我做不到。我可能可以重新设计Promotion类,将需要它的静态成员更改为实例成员,但它们是静态的有其原因——如果所有静态成员都必须在每次实例化new Promotion时定义在每个实例上,那么就会存在大量的性能损耗。

有什么可能的解决办法?我没有意识到HttpContext.Current会在Parallel.ForEach()的范围内为空。


Promotion的静态成员只会在第一次访问类代码时进行初始化...它们怎么可能依赖于HttpContext.Current - user180326
Promotion 的源代码是否在您的控制之下? - user180326
4个回答

11

HttpContext.Current为空是因为它运行在“非 Web 线程”中。如果你使用new Thread(...)分叉一些代码,情况会完全相同。TPL(任务并行库)在某种程度上隐藏了这一点,但你仍然需要意识到Parallel.ForEach中的每个迭代可能在不同的线程中运行,并据此进行处理。

特别地,如果你想在 Web 请求之外使用某些类或方法(而 Parallel.ForEach 就是这样的用法),就不能使用 HttpContext.Current。解决方法是在构造函数(或作为方法参数)中显式传递 HttpContext(或 HttpContextBase,以提高可测试性)。

简而言之:你需要摆脱对 HttpContext.Current 的静态使用。


4

只需将Parallel.ForEach调用外部的任何上下文传递到内部依赖于该上下文的任何函数中即可。

var context = HttpContext.Current;
Parallel.ForEach(items, item =>
    {
        DoSomething(item, context);
    }
);



private static void DoSomething(item, context = null) {
    if (context == null) context = HttpContext.Current;

    ...
}

我希望在代码中设置空值回退,这样我就不必一直担心传递上下文的问题。只有在从另一个线程调用时,我才需要记住我的函数需要上下文,然后将其添加到其中。


0

这个不起作用,因为在foreach循环内部创建了一个新的线程,所以上下文为空。即使创建一个名为DoSomething的方法来设置当前上下文,上下文仍然为空。


0

正如 Mauricio 指出的那样,HttpContext.Current 取决于当前执行的线程。对我来说,一个 静态构造函数 依赖于一个如此内在瞬态的值 HttpContext.Current,这似乎很不寻常,但也许这不是你的想法。

如果您可以更改 Promotion 类,那将是我首选的第一种选择。

如果不能更改,则需要以某种方式在 HttpContext.Current 仍然有效的时候强制进行 Promotion 的类型初始化。要了解是什么触发了类型初始化,请参阅 this Jon Skeet 博客文章

一个选项是创建一个虚拟的 Promotion 对象(整个程序中只需一次)。如果这不是一个选项,您可以尝试通过反射读取属性。我不知道这是否会强制进行类型初始化,但我猜可能会。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接