我正在编写一个多线程应用程序,其中一部分代码必须是线程安全的,以便跟踪任务编号。
我有这个方法:
private void IncrementTaskNumber() {
Interlocked.Increment(ref _TaskNumber);
}
_TaskNumber是同一个类中的私有int。问题是,这会抛出一个“属性,索引器或动态成员访问可能不会作为out或ref参数传递”异常。 为了解决这个问题,我做了:
private void IncrementTaskNumber() {
int _taskNum = _TaskNumber;
Interlocked.Increment(ref _taskNum);
_TaskNumber = _taskNum;
}
这仍然是线程安全吗?
答案 0 :(得分:3)
_TaskNumber是同一个类中的私有int。
_TaskNumber
必须是私有字段才能实现此目的。您可能将其作为私人财产。
将其定义为:
private int _TaskNumber;
它会起作用。
另请注意,您当前的解决方法会引入竞争条件 - 您可以通过使用临时变量来有效地消除原子增量,这首先会破坏使用Interlocked
的目的。您需要直接增加字段。
答案 1 :(得分:1)
IncrementTaskNumber
方法似乎没有锁定机制。除非你只从一个地方打电话,否则它不是线程安全的。您想要做的第一个实现,即使它可以工作也不会,因为Interlocked.Increment(ref _TaskNumber);
可能在第一次执行之前第二次被调用,并且写入ref参数。
编辑: 如果你想让它成为线程安全的,你可以像这样修改yoru方法:
private void IncrementTaskNumber()
{
lock (_TaskNumber)
_TaskNumber++;
}
编辑2 :(如果使用lock
对您的应用程序来说过于昂贵,您可能需要查看其他解决方案。)
答案 2 :(得分:0)
这绝对不是线程安全的:
private void IncrementTaskNumber() {
int _taskNum = _TaskNumber;
Interlocked.Increment(ref _taskNum);
_TaskNumber = _taskNum;
}
因为执行序列的线程可能在这3个操作中的每个操作之间被中断。除非你引入一个循环,如果在此期间本地值发生变化,仍会继续重试,但这将无效,但这基本上意味着你要使用Interlocked.Increment
重新实现Interlocked.Increment
。 :)
只需将_TaskNumber
设为成员变量,而不是属性。