【发布时间】:2017-05-25 11:30:32
【问题描述】:
我有使用System.Threading.Task 调用服务函数的操作方法。
服务函数有一个静态全局变量,我设置了属性ThreadStatic 以使我的函数线程安全。
我的问题是,有时当我运行我的操作方法时,共享变量_sharedList 在我的HotelService 中访问它时会引发空引用异常。
这是一个复制问题的示例实现:
调用 HotelService.TestMultiThread 的控制器操作方法
public ActionResult MultiThread()
{
HotelService svc = new HotelService();
var resp = new List<TestPnrHeaderResponse>();
var tasks = Enumerable.Range(0, 5).Select(i => Task.Run(() => svc.TestMultiThread(i)));
var results = await Task.WhenAll(tasks);
return View(resp);
}
HotelService 类
_sharedList 在
_sharedList.listInt.AddRange(GetIntList());行中为 Null
public class HotelService
{
[ThreadStatic]
private static TestPnrHeaderResponse _sharedList;
private void LoadCache()
{
_sharedList = new TestPnrHeaderResponse();
_sharedList.PnrLegs = new List<PnrLegVM>();
_sharedList.listInt = new List<int>();
Task.Factory.StartNew(() =>
{
_sharedList.listInt.AddRange(GetIntList());
});
}
private IEnumerable<int> GetIntList()
{
return Enumerable.Range(0, 5);
}
public TestPnrHeaderResponse TestMultiThread(int count)
{
LoadCache();
if (count % 2 == 0)
{
_sharedList.PnrLegs.Add(new PnrLegVM
{
ApplicationType = count.ToString(),
PKCity = count,
PKNationality = 1,
PKPnrHeader = 1,
PKPnrLeg = 1
});
}
else
{
_sharedList.PnrLegs.Add(new PnrLegVM
{
ApplicationType = count.ToString(),
PKCity = count,
PKNationality = 99,
PKPnrHeader = 99,
PKPnrLeg = 99
});
}
return _sharedList;
}
}
共享变量类
public class TestPnrHeaderResponse
{
public List<PnrLegVM> PnrLegs { get; set; }
public List<int> listInt { get; set; }
}
public class PnrLegVM
{
public int PKPnrLeg{get;set;}
public int PKPnrHeader{get;set;}
public string ApplicationType{get;set;}
public int PKNationality {get;set;}
}
请帮助寻找解决方案。此外,是否有更好的方法使函数线程安全,因为它在实际实现中使用了许多共享变量。
【问题讨论】:
-
任务不是线程。它们是在可重用线程池线程上运行的作业。这意味着将任何内容存储在 ThreadStatic 存储中几乎可以保证您会丢失它。无法保证您的任何任务都会重用同一个线程
-
您的代码也过于复杂。没有理由使用冷任务或将它们放在数组中。它们不是线程。如果您想在后台执行 5 次调用,只需使用 5 次
Task.Run调用并使用await Task.WhenAll(...);等待结果。 -
@PanagiotisKanavos 我知道任务不是线程。正如我所问的那样,我正在尝试为
ThreadStatic找到更好的解决方案。 -
然而,你是这样对待它们的,通过创建冷任务,将它们存储在数组中,调用
.Start,使用ThreadStatic,当你不知道你的线程是没有意义的正在运行。你为什么想要ThreadStatic呢?你想做什么?如果这是您的意图,它不会使列表成为线程安全的。 -
如果您尝试延迟初始化列表,请使用
Lazy<T>。要生成 5 个正在运行的任务,请使用var tasks=Enumerable.Range(0,5).Select(int i=>Task.Run(()=>svc.TestMultiThread(i));var results=await Task.WhenAll(tasks);。或者您可以使用 PLINQ - 只需在查询中使用AsParallel()。
标签: c# asp.net-mvc multithreading thread-safety