ASP.NET HostedService 线程安全单例
ASP.NET HostedService thread safe singleton
当 read/writing 到单例中使用的 List
中的项目时,我需要关心线程安全吗?
如果是这样,我需要锁定 Foo.Value
或 FooManager.FooList
的 getter/setter 吗?
我知道读取和添加项目到 List
不是线程安全的,但我不确定这是否也适用于我的代码。
示例代码:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
var manager = new FooManager();
manager.FooList.Add(new Foo());
manager.FooList.Add(new Foo());
manager.FooList.Add(new Foo());
var updater = new FooUpdater(manager);
updater.UpdateAsync();
}
}
public class FooManager // Singleton
{
public List<Foo> FooList {get;set;}
public FooManager()
{
FooList = new List<Foo>();
}
}
public class Foo
{
public string Value {get; set;}
public async Task UpdateValue(string value)
{
await Task.Delay(10000);
Value = value;
}
}
public class FooUpdater // HostedService
{
private readonly FooManager _foomanager;
public FooUpdater(FooManager foomanager)
{
_foomanager = foomanager;
}
public async Task UpdateAsync()
{
while(true)
{
foreach(var foo in _foomanager.FooList)
{
await foo.UpdateValue("AAA");
}
}
}
}
public class FooController // Api Controller
{
private readonly FooManager _fooManager;
public FooController(FooManager foomanager)
{
_fooManager = foomanager;
}
// ...
// Read or write to foomanager.FooList items value
}
您的代码中存在 2 个潜在的线程安全问题
Writing/Updating 到 List<T>
同时来自多个线程。
解决方案: 使用 ConcurrentQueue<T>
(请参阅下面的@TheodorZoulias 评论)而不是 List<T>
,因为您不会通过索引访问项目,这是完美契合。
有可能 foo
对象同时从 HostedService
和 FooController
更新 (foo.UpdateValue(string)
)。
解决方案: 你需要使 foo class 中的 UpdateValue
方法是线程安全的。抱歉,我无法为您提供示例代码,因为您没有在 post.
中提供实际的(比实际更简单的)代码
If so, do I need to lock the getter/setter of Foo.Value or FooManager.FooList?
锁定 FooManager.FooList
的 getter/setter 没有用。如果类型不是具有原子性 reads/writes 的类型(例如 double
),则应该锁定 Foo.Value
,更好地锁定所有类型,但我认为如果我们没有锁定,所以我个人通常不会锁定,因为我不关心线程之间的值一致性。
当 read/writing 到单例中使用的 List
中的项目时,我需要关心线程安全吗?
如果是这样,我需要锁定 Foo.Value
或 FooManager.FooList
的 getter/setter 吗?
我知道读取和添加项目到 List
不是线程安全的,但我不确定这是否也适用于我的代码。
示例代码:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
var manager = new FooManager();
manager.FooList.Add(new Foo());
manager.FooList.Add(new Foo());
manager.FooList.Add(new Foo());
var updater = new FooUpdater(manager);
updater.UpdateAsync();
}
}
public class FooManager // Singleton
{
public List<Foo> FooList {get;set;}
public FooManager()
{
FooList = new List<Foo>();
}
}
public class Foo
{
public string Value {get; set;}
public async Task UpdateValue(string value)
{
await Task.Delay(10000);
Value = value;
}
}
public class FooUpdater // HostedService
{
private readonly FooManager _foomanager;
public FooUpdater(FooManager foomanager)
{
_foomanager = foomanager;
}
public async Task UpdateAsync()
{
while(true)
{
foreach(var foo in _foomanager.FooList)
{
await foo.UpdateValue("AAA");
}
}
}
}
public class FooController // Api Controller
{
private readonly FooManager _fooManager;
public FooController(FooManager foomanager)
{
_fooManager = foomanager;
}
// ...
// Read or write to foomanager.FooList items value
}
您的代码中存在 2 个潜在的线程安全问题
Writing/Updating 到
List<T>
同时来自多个线程。解决方案: 使用
ConcurrentQueue<T>
(请参阅下面的@TheodorZoulias 评论)而不是List<T>
,因为您不会通过索引访问项目,这是完美契合。有可能
foo
对象同时从HostedService
和FooController
更新 (foo.UpdateValue(string)
)。解决方案: 你需要使 foo class 中的
中提供实际的(比实际更简单的)代码UpdateValue
方法是线程安全的。抱歉,我无法为您提供示例代码,因为您没有在 post.
If so, do I need to lock the getter/setter of Foo.Value or FooManager.FooList?
锁定 FooManager.FooList
的 getter/setter 没有用。如果类型不是具有原子性 reads/writes 的类型(例如 double
),则应该锁定 Foo.Value
,更好地锁定所有类型,但我认为如果我们没有锁定,所以我个人通常不会锁定,因为我不关心线程之间的值一致性。