我正在尝试理解C#Volatile类.
正如我读到的:
该Volatile.Write方法强制将位置中的值写入调用点.此外,任何早期的程序订单加载和存储必须在调用Volatile.Write之前发生.
该Volatile.Read方法强制在呼叫点读取位置中的值.此外,任何后续的程序订单加载和存储必须在调用Volatile.Read之后发生.
这是否意味着:
internal sealed class ThreadsSharingData {
private Int32 m_flag = 0;
private Int32 m_value = 0;
// This method is executed by one thread
public void Thread1() {
// Note: 5 must be written to m_value before 1 is written to m_flag
m_value = 5;
Volatile.Write(ref m_flag, 1);
}
// This method is executed by another thread
public void Thread2() {
// Note: m_value must be read after m_flag is read
if …Run Code Online (Sandbox Code Playgroud) 我有一些代码,在新线程上抛出异常,我需要在主线程上确认和处理.为了实现这一点,我通过使用保存抛出异常的字段来共享线程之间的状态.
我的问题是在检查null时是否需要使用锁,因为我在以下代码示例中进行操作?
public class MyClass
{
readonly object _exceptionLock = new object();
Exception _exception;
public MyClass()
{
Task.Run(() =>
{
while (CheckIsExceptionNull())
{
// This conditional will return true if 'something has gone wrong'.
if(CheckIfMyCodeHasGoneWrong())
{
lock(_exceptionLock)
{
_exception = new GoneWrongException();
}
}
}
});
}
bool CheckIsExceptionNull() // Is this method actually necessary?
{
lock (_exceptionLock)
{
return _exception == null;
}
}
// This method gets fired periodically on the Main Thread.
void RethrowExceptionsOnMainThread()
{ …Run Code Online (Sandbox Code Playgroud) 在多CPU机器上运行的多线程程序中,我需要使用易失性读/写来访问共享状态(下面的示例代码中的_data)以确保正确性.
换句话说,可以在cpu上缓存堆对象吗?
使用下面的示例并假设多线程将访问GetValue和Add方法,我需要ThreadA能够添加数据(使用Add方法)和ThreadB能够立即查看/获取添加的数据(使用GetValue方法) ).那么我需要向_data添加易失性读/写以确保这一点吗?基本上我不想添加要在ThreadA的cpu上缓存的数据.
/我不是锁定(强制独占线程访问),因为代码需要超快,我不从_data中删除任何数据,所以我不需要锁定_data.
谢谢.
****更新****************************
显然你们认为使用这个例子无锁是不好的主意.但是我可以在这里面对哪些副作用或例外?
如果1个线程正在迭代read值而另一个线程正在迭代update的值,那么Dictionary类型会抛出异常吗?或者我只会遇到"脏读"(在我的情况下会没问题)?
****结束更新****************************
public sealed class Data
{
private volatile readonly Dictionary<string, double> _data = new Dictionary<string, double>();
public double GetVaule(string key)
{
double value;
if (!_data.TryGetValue(key, out value))
{
throw new ArgumentException(string.Format("Key {0} does not exist.", key));
}
return value;
}
public void Add(string key, double value)
{
_data.Add(key, value);
}
public void Clear()
{
_data.Clear();
}
}
Run Code Online (Sandbox Code Playgroud)
谢谢你的回复.关于锁,这些方法几乎经常被多个线程调用,所以我的问题是有争议的锁而不是实际的锁操作.
所以我的问题是关于cpu缓存,可以将堆对象(_data实例字段)缓存在cpu上吗?我是否需要使用易失性读/写访问_data字段?
/另外,我坚持使用.Net 2.0.
谢谢你的帮助.
做Volatile.Read和Volatile.Write对非易失性INT正常读取和任务对同一个int完全相同的效果volatile修改?
其动机是防止volatile变量不应通过ref参数传递给Interlocked方法.(我知道程序是正确的,尽管有警告,但我宁愿不在我的代码中留下神秘的编译指示.)
//private volatile int suppressListChangedEvents;
private int suppressListChangedEvents;
public void SuppressListChangedEvents()
{
Interlocked.Increment(ref suppressListChangedEvents);
}
public void UnsuppressListChangedEvents()
{
if (Interlocked.Decrement(ref suppressListChangedEvents) < 0)
throw new InvalidOperationException("Too many calls to unsuppress.");
}
protected override void OnListChanged(ListChangedEventArgs e)
{
//if (suppressListChangedEvents != 0) return;
if (Volatile.Read(ref suppressListChangedEvents) != 0) return;
base.OnListChanged(e);
}
Run Code Online (Sandbox Code Playgroud)
同样,我有一个使用命令的DirectX渲染线程泵,Interlocked.Exchange(ref command, null)并且使用直接赋值给volatile command变量生成一些命令.我可以安全地将其更改为Volatile.Write并删除volatile修饰符而不会引入开销吗?
编辑:确定的答案.接下来,我将避开修饰符并始终通过Volatile和Interlocked显式访问变量.这样,代码对访问类型没有任何歧义; 改变变量声明不会改变代码的含义.
在一个完美的世界中,我会volatile通过让编译器拒绝直接引用或赋值给变量来使修饰符变得有用.这将迫使我将引用传递给Volatile或Interlocked或者本身使用Volatile或Interlocked的方法.我应该抓住Roslyn的副本.
如果我有一个抛出异常的任务,我可以检查继续是否有异常:
Task task1 = Task.Factory.StartNew (() => { throw null; });
Task task2 = task1.ContinueWith (ant => Console.Write (ant.Exception));
Run Code Online (Sandbox Code Playgroud)
但我也知道:
如果先行者抛出并且延续未能查询前件的Exception属性(并且未等待前提),则该异常被视为未处理且 应用程序死亡.
所以我尝试过:
Task task1 = Task.Factory.StartNew (() => { throw null; });
Task task2 = task1.ContinueWith (ant => Console.Write (1));//1
Run Code Online (Sandbox Code Playgroud)
但应用程序没有崩溃.
拜托,我错过了什么?