【发布时间】:2017-06-22 22:15:23
【问题描述】:
我一直在开发多线程算法,我对在 C# 中的线程之间共享类成员有些疑问。
假设我们有两个类算法和处理器。处理器有一个 main 方法 DoWork 和附加的 AvaliableResources 方法,该方法偶尔调用以更改可用于处理的资源的数量。 Algorithm 对象中的方法 Run 和 UpdateResources 由两个不同的线程调用,可能在不同的核心上工作。
是否有可能 _processor 变量将存储在 CPU 的缓存中并且永远不会上传到内存中,并且永远不会调用 AvaliableResources 因为 _processor 第二次为空线程?
class Processor
{
public void DoWork() { ... }
public void AvaliableResources(int x) { ... }
}
class Algorithm
{
private Processor _processor;
public void Run()
{
_processor = new Processor();
_processor.DoWork();
_processor = null;
}
public void UpdateResources(int x)
{
_processor?.AvaliableResources(x);
}
}
如果存在同步问题,以下代码是否可以解决?
备选方案 1
class Processor
{
public void DoWork() { ... }
public void UpdateResources(int x) { ... }
}
class Algorithm
{
private volatile Processor _processor; // added volatile keyword
public void Run()
{
_processor = new Processor();
_processor.DoWork();
_processor = null;
}
public void UpdateResources(int x)
{
_processor?.UpdateResources(x);
}
}
备选方案 2
class Processor
{
public void DoWork() { ... }
public void UpdateResources(int x) { ... }
}
class Algorithm
{
private Processor _processor;
public void Run()
{
_processor = new Processor();
Thread.MemoryBarrier(); // Added memory barier
_processor.DoWork();
_processor = null;
}
public void UpdateResources(int x)
{
Thread.MemoryBarrier(); // Added memory barier
_processor?.UpdateResources(x);
}
}
编辑: 正如您在 cmets 中所建议的,请查看更好的解释代码:
class Processor
{
private int resources = Environment.ProcessorCount;
public void DoWork()
{
/*do some long running job using avaliable resources*/
}
public void UpdateResources(int x)
{
resources = x;
}
}
class Algorithm
{
private volatile Processor _processor;
public void Run()
{
_processor = new Processor();
_processor.DoWork();
_processor = null;
}
public void UpdateResources(int x)
{
_processor?.UpdateResources(x);
}
}
class Program
{
static void Main(string[] args)
{
var algorithm = new Algorithm();
var result = Task.Run(() => algorithm.Run());
// The resources were required for other staff and are reduced
algorithm.UpdateResources(1);
// The resources are reassigned for the long running algorithm
algorithm.UpdateResources(10);
// wait until the algorithm finishes
result.Wait();
// this update should have no effect as the Run method has finished and _processor is null
algorithm.UpdateResources(10);
}
}
【问题讨论】:
-
运行代码时发生了什么?
-
还要考虑stackoverflow.com/questions/3556351/… 是否无法解决您的问题
-
"for the second thread" - 两个线程都不是第一个或第二个线程 - 它们只是不同的线程; “第二个”线程可以在
Run甚至被调用之前完成它需要做的所有事情......在这种情况下:_processor的值是什么? -
@Fabulous 看起来很相似,但我担心它是否也适用于引用类型。他们正在讨论布尔标志,它是值类型,在另一种类型上,引用是一个指针,它只是一个值。因此我的直觉说行为应该是相同的,我应该使用 MemoryBarrier 来确保可移植性。
-
@MarcGravell 我知道,这只是我必须调用线程#1 和线程#2 的术语。而且它与执行顺序无关:)。
标签: c# multithreading thread-safety clr