【发布时间】:2017-08-14 13:24:27
【问题描述】:
我有一个 FooContext 类,它从 ASP.NET Web API 应用程序内的请求中捕获一些特定于 HTTP 请求的运行时值
public class FooContext
{
private readonly ISet<string> _set = new HashSet<string>();
public void AddToSet(string s) => _set.Add(s);
// Copied so that caller won't modify _set
public ISet<string> GetStrings() => new HashSet<string>(_set);
}
多个消费者依赖这个FooContext,会调用AddToSet/GetStrings,根据结果,运行不同的业务逻辑。
我想保证每个 HTTP 请求只有一个 FooContext 实例,所以我在 DI 容器中注册为请求范围(此处使用 Autofac 作为示例,但我猜大多数容器大致相同):
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<FooContext>().InstancePerRequest();
}
我的理解是,FooContext 不是线程安全的,因为线程可能在同一个 FooContext 实例上同时调用 GetStrings/AddToSet(因为它是请求范围的)。 不保证每个 HTTP 请求都会在一个线程上完成。
我没有明确地创建新线程,也没有在我的应用程序中调用Task.Run(),但我确实使用了很多async-await 和ConfigureAwait(false),这意味着延续可能在不同的线程上。
我的问题是:
-
FooContext真的不是线程安全的吗?我上面的理解正确吗? - 如果这确实是线程不安全的,并且我想允许多个读者但只有一个独占作者,我应该在
ISet<string>上应用ReaderWriterLockSlim吗?
更新
由于评论者认为我的问题在没有显示FooContext 的用法的情况下无法回答,所以我会在这里进行。我在IAutofacActionFilter 中使用FooContext 来捕获在控制器方法中传递的几个参数:
public class FooActionFilter : IAutofacActionFilter
{
private readonly FooContext _fooContext;
public FooActionFilter(FooContext fooContext)
=> _fooContext = fooContext;
public Task OnActionExecutingAsync(
HttpActionContext actionContext,
CancellationToken cancellationToken)
{
var argument = (string)actionContext.ActionArguments["mystring"];
_fooContext.AddToSet(argument);
return Task.CompletedTask;
}
}
然后在控制业务逻辑的其他服务类中:
public class BarService
{
private readonly FooContext _fooContext;
public BarService(FooContext fooContext)
=> _fooContext = fooContext;
public async Task DoSomething()
{
var strings = _fooContext.GetStrings();
if (strings.Contains("foo"))
{
// Do something
}
}
}
【问题讨论】:
-
1. Yes.在实践中,只要您一次只针对给定的 HTTP 请求运行一个线程(换句话说 - 它问题不是在请求的整个生命周期内使用的多个线程 - 它是两个线程同时访问它,这将导致线程问题)。 您是否考虑过使用ConcurrentDictionary而不是HashSet? -
@mjwills 感谢您的评论。我确实考虑过使用
ConcurrentDictionary,但由于我实际上没有键值对,我决定坚持使用ISet。 -
您可以忽略这些值(或将它们设置为 null) - 在这种情况下,
ConcurrentDictionary基本上是ConcurrentHashSet。
标签: c# asp.net multithreading asp.net-web-api autofac