Vac*_*ano 7 .net c# thread-safety
假设我有几个List属性.像这样的东西:
List<CustomerTypes> CustomerTypes {get; set;}
List<FormatTypes> FormatTypes {get; set;}
List<WidgetTypes> WidgetTypes {get; set}
List<PriceList> PriceList {get; set;}
Run Code Online (Sandbox Code Playgroud)
因为这些值很少更新,所以我在启动时将它们缓存在我的WCF服务中.然后我有一个服务操作,可以调用它来刷新它们.
服务操作将从数据库中查询所有这些内容,如下所示:
// Get the data from the database.
var customerTypes = dbContext.GetCustomerTypes();
var formatTypes = dbContext.GetFormatTypes();
var widgetTypes = dbContext.GetWidgetTypes ();
var priceList = dbContext.GetPriceList ();
// Update the references
CustomerTypes = customerTypes;
FormatTypes = formatTypes;
WidgetTypes = widgetTypes;
PriceList = priceList;
Run Code Online (Sandbox Code Playgroud)
这导致很少有时间这些并非全部同步.但是,它们并非完全线程安全.(呼叫可以访问新的CustomerType和旧的PriceList.)
我怎样才能这样做,以便在更新引用时,对这些列表的任何使用必须等到所有引用都更新后?
小智 0
我认为将一些东西放在一起作为示例会很有趣。此答案基于 Marc Gravell 的答案(此处)的指导。
以下类接受毫秒值并提供一个事件来通知调用者刷新间隔已达到。
它使用Environment.TickCount,它比使用DateTime 对象快几个数量级。
双重检查锁可防止多个线程同时刷新,并受益于避免每次调用上的锁而减少的开销。
使用 Task.Run() 刷新 ThreadPool 上的数据允许调用者不间断地继续使用现有的缓存数据。
using System;
using System.Threading.Tasks;
namespace RefreshTest {
public delegate void RefreshCallback();
public class RefreshInterval {
private readonly object _syncRoot = new Object();
private readonly long _interval;
private long _lastRefresh;
private bool _updating;
public event RefreshCallback RefreshData = () => { };
public RefreshInterval(long interval) {
_interval = interval;
}
public void Refresh() {
if (Environment.TickCount - _lastRefresh < _interval || _updating) {
return;
}
lock (_syncRoot) {
if (Environment.TickCount - _lastRefresh < _interval || _updating) {
return;
}
_updating = true;
Task.Run(() => LoadData());
}
}
private void LoadData() {
try {
RefreshData();
_lastRefresh = Environment.TickCount;
}
catch (Exception e) {
//handle appropriately
}
finally {
_updating = false;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)Interlocked 提供了缓存数据的快速、原子替换。
using System.Collections.Generic;
namespace RefreshTest {
internal static class ContextCache {
private static readonly RefreshInterval _refresher = new RefreshInterval(60000);
private static List<int> _customerTypes = new List<int>();
static ContextCache() {
_refresher.RefreshData += RefreshData;
}
internal static List<int> CustomerTypes {
get {
_refresher.Refresh();
return _customerTypes;
}
}
private static void RefreshData() {
List<int> customerTypes = new List<int>(); //dbContext.GetCustomerTypes();
Interlocked.Exchange(ref _customerTypes, customerTypes);
}
}
}
Run Code Online (Sandbox Code Playgroud)
数百万个并发调用运行约 100 毫秒(不过请运行您自己的测试!):
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
namespace RefreshTest {
internal class Program {
private static void Main(string[] args) {
Stopwatch watch = new Stopwatch();
watch.Start();
List<Task> tasks = new List<Task>();
for (int i = 0; i < Environment.ProcessorCount; i++) {
Task task = Task.Run(() => Test());
tasks.Add(task);
}
tasks.ForEach(x => x.Wait());
Console.WriteLine("Elapsed Milliseconds: {0}", watch.ElapsedMilliseconds);
Console.ReadKey();
}
private static void Test() {
for (int i = 0; i < 1000000; i++) {
var a = ContextCache.CustomerTypes;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
希望有帮助。
| 归档时间: |
|
| 查看次数: |
555 次 |
| 最近记录: |