C# 无锁线程安全状态同步?
首先,我知道这样的问题: 。。。但我仍然不确定是否可以在我的案例中避免使用lock(){} 在我的例子中,我有一个表示某种状态的类,只有一个线程会不时修改该状态。但是有许多线程读取状态 是否需要在state对象上使用Interlocked.Exchange()? 我一定要使用lock(){} 下面是我的示例代码,简化到最低限度:C# 无锁线程安全状态同步?,c#,multithreading,thread-safety,thread-synchronization,C#,Multithreading,Thread Safety,Thread Synchronization,首先,我知道这样的问题: 。。。但我仍然不确定是否可以在我的案例中避免使用lock(){} 在我的例子中,我有一个表示某种状态的类,只有一个线程会不时修改该状态。但是有许多线程读取状态 是否需要在state对象上使用Interlocked.Exchange()? 我一定要使用lock(){} 下面是我的示例代码,简化到最低限度: using System; using System.Collections.Generic; using System.Threading; using Syste
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MultiThreadingExample
{
class State
{
public int X { get; set; }
public string Str { get; set; }
public DateTime Current { get; set; }
}
class Example
{
State state;
CancellationTokenSource cts = new CancellationTokenSource();
Task updater;
List<Task> readers = new List<Task>();
public void Run()
{
updater = Task.Factory.StartNew(() =>
{
while (!cts.Token.IsCancellationRequested)
{
// wait until we have a new state from some source
Thread.Sleep(1000);
var newState = new State() { Current = DateTime.Now, X = DateTime.Now.Millisecond, Str = DateTime.Now.ToString() };
// critical part
state = newState;
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
for (int i = 0; i < 10; i++)
{
readers.Add(Task.Factory.StartNew(() =>
{
while (!cts.Token.IsCancellationRequested)
{
// critical part
var readState = state;
// use it
if (readState != null)
{
Console.WriteLine(readState.Current);
Console.WriteLine(readState.Str);
Console.WriteLine(readState.X);
}
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default));
}
}
}
class Program
{
static void Main(string[] args)
{
new Example().Run();
Console.ReadKey();
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统线程;
使用System.Threading.Tasks;
名称空间多线程示例
{
阶级国家
{
公共整数X{get;set;}
公共字符串Str{get;set;}
公共日期时间当前{get;set;}
}
课例
{
国家;
CancellationTokenSource cts=新的CancellationTokenSource();
任务更新程序;
列表阅读器=新列表();
公开募捐
{
updater=Task.Factory.StartNew(()=>
{
而(!cts.Token.IsCancellationRequested)
{
//等到我们从某个来源得到一个新的状态
睡眠(1000);
var newState=newState(){Current=DateTime.Now,X=DateTime.Now.毫秒,Str=DateTime.Now.ToString()};
//关键部分
状态=新闻状态;
}
},cts.Token,TaskCreationOptions.LongRunning,TaskScheduler.Default);
对于(int i=0;i<10;i++)
{
readers.Add(Task.Factory.StartNew(()=>
{
而(!cts.Token.IsCancellationRequested)
{
//关键部分
var readState=状态;
//使用它
if(readState!=null)
{
Console.WriteLine(readState.Current);
Console.WriteLine(readState.Str);
Console.WriteLine(readState.X);
}
}
},cts.Token,TaskCreationOptions.LongRunning,TaskScheduler.Default));
}
}
}
班级计划
{
静态void Main(字符串[]参数)
{
新示例().Run();
Console.ReadKey();
}
}
}
如果您只有一个正在更新的线程和一个正在读取的线程,我相信您不会遇到任何运行时错误。然而,您的示例显示了10个读卡器线程。话虽如此,我认为您不应该假设您不需要任何东西来确保应用程序线程安全。您应该至少引入锁定,以确保线程之间能够很好地配合。因为当您读取读取器线程中的值时,状态对象是一个复杂的对象,所以您可能无法获得预期的所有内容。在读取操作期间,如果不锁定,可能会更改一个或两个属性,但不会更改第三个属性。下面是我正在谈论的修改示例
class State
{
public int X { get; set; }
public string Str { get; set; }
public DateTime Current { get; set; }
}
class Example
{
State state;
CancellationTokenSource cts = new CancellationTokenSource();
Object syncObj = new Object();
Task updater;
List<Task> readers = new List<Task>();
public void Run()
{
updater = Task.Factory.StartNew(() =>
{
while (!cts.Token.IsCancellationRequested)
{
// wait until we have a new state from some source
Thread.Sleep(1000);
var newState = new State() { Current = DateTime.Now, X = DateTime.Now.Millisecond, Str = DateTime.Now.ToString() };
// critical part
lock(syncObj) {
state = newState;
}
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
for (int i = 0; i < 10; i++)
{
readers.Add(Task.Factory.StartNew(() =>
{
while (!cts.Token.IsCancellationRequested)
{
State readState = null;
// critical part
lock(syncObj) {
readState = state.Clone();
}
// use it
if (readState != null)
{
Console.WriteLine(readState.Current);
Console.WriteLine(readState.Str);
Console.WriteLine(readState.X);
}
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default));
}
}
}
class Program
{
static void Main(string[] args)
{
new Example().Run();
Console.ReadKey();
}
}
类状态
{
公共整数X{get;set;}
公共字符串Str{get;set;}
公共日期时间当前{get;set;}
}
课例
{
国家;
CancellationTokenSource cts=新的CancellationTokenSource();
Object syncObj=新对象();
任务更新程序;
列表阅读器=新列表();
公开募捐
{
updater=Task.Factory.StartNew(()=>
{
而(!cts.Token.IsCancellationRequested)
{
//等到我们从某个来源得到一个新的状态
睡眠(1000);
var newState=newState(){Current=DateTime.Now,X=DateTime.Now.毫秒,Str=DateTime.Now.ToString()};
//关键部分
锁(同步对象){
状态=新闻状态;
}
}
},cts.Token,TaskCreationOptions.LongRunning,TaskScheduler.Default);
对于(int i=0;i<10;i++)
{
readers.Add(Task.Factory.StartNew(()=>
{
而(!cts.Token.IsCancellationRequested)
{
State readState=null;
//关键部分
锁(同步对象){
readState=state.Clone();
}
//使用它
if(readState!=null)
{
Console.WriteLine(readState.Current);
Console.WriteLine(readState.Str);
Console.WriteLine(readState.X);
}
}
},cts.Token,TaskCreationOptions.LongRunning,TaskScheduler.Default));
}
}
}
班级计划
{
静态void Main(字符串[]参数)
{
新示例().Run();
Console.ReadKey();
}
}
这是一个很小的更改,但它将确保您在状态对象方面是线程安全的。如果您只有一个正在更新的线程和一个正在读取的线程,我相信您不会遇到任何运行时错误。然而,您的示例显示了10个读卡器线程。话虽如此,我认为您不应该假设您不需要任何东西来确保应用程序线程安全。您应该至少引入锁定,以确保线程之间能够很好地配合。因为当您读取读取器线程中的值时,状态对象是一个复杂的对象,所以您可能无法获得预期的所有内容。在读取操作期间,如果不锁定,可能会更改一个或两个属性,但不会更改第三个属性。下面是我正在谈论的修改示例
class State
{
public int X { get; set; }
public string Str { get; set; }
public DateTime Current { get; set; }
}
class Example
{
State state;
CancellationTokenSource cts = new CancellationTokenSource();
Object syncObj = new Object();
Task updater;
List<Task> readers = new List<Task>();
public void Run()
{
updater = Task.Factory.StartNew(() =>
{
while (!cts.Token.IsCancellationRequested)
{
// wait until we have a new state from some source
Thread.Sleep(1000);
var newState = new State() { Current = DateTime.Now, X = DateTime.Now.Millisecond, Str = DateTime.Now.ToString() };
// critical part
lock(syncObj) {
state = newState;
}
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
for (int i = 0; i < 10; i++)
{
readers.Add(Task.Factory.StartNew(() =>
{
while (!cts.Token.IsCancellationRequested)
{
State readState = null;
// critical part
lock(syncObj) {
readState = state.Clone();
}
// use it
if (readState != null)
{
Console.WriteLine(readState.Current);
Console.WriteLine(readState.Str);
Console.WriteLine(readState.X);
}
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default));
}
}
}
class Program
{
static void Main(string[] args)
{
new Example().Run();
Console.ReadKey();
}
}
类状态
{
公共整数X{get;set;}
公共字符串Str{get;set;}
公共日期时间当前{get;set;}
}
课例
{
国家;
CancellationTokenSource cts=新的CancellationTokenSource();
Object syncObj=新对象();
任务更新
// read and swap with null atomically
var readState = Interlocked.Exchange(ref state, null);
BlockingCollection<State> queue = new BlockingCollection<State>();
updater = Task.Factory.StartNew(() =>
{
while (!cts.Token.IsCancellationRequested)
{
Thread.Sleep(1000);
var newState = GetNewState();
queue.Add(newState);
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
for (int i = 0; i < 10; i++)
{
var readerId = i.ToString();
readers.Add(Task.Factory.StartNew(() =>
{
while (!cts.Token.IsCancellationRequested)
{
// get it
var readState = queue.Take(cts.Token);
// use it
if (readState != null)
{
Console.WriteLine("Hello from reader #" + readerId);
Console.WriteLine(readState.X);
}
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default));
}