1

我正在编写一个多线程应用程序,其中一部分代码必须是线程安全的才能跟踪任务编号。

我有这个方法:

private void IncrementTaskNumber() {
   Interlocked.Increment(ref _TaskNumber);
}

_TaskNumber 是同一类中的私有 int。问题是,这会引发“A 属性、索引器或动态成员访问可能不会作为 out 或 ref 参数传递”异常。为了解决这个问题,我这样做:

private void IncrementTaskNumber() {
   int _taskNum = _TaskNumber;
   Interlocked.Increment(ref _taskNum);
   _TaskNumber = _taskNum;
}

这仍然是线程安全的吗?

4

3 回答 3

3

_TaskNumber 是同一类中的私有 int。

_TaskNumber必须是一个私有领域才能工作。您可能将其作为私有财产。

将其定义为:

private int _TaskNumber;

它会起作用。

另请注意,您当前的解决方法引入了竞争条件 - 您通过使用临时变量来有效地摆脱原子增量,这首先破坏了使用的目的Interlocked。您需要直接增加该字段。

于 2012-08-27T16:20:29.067 回答
1

您似乎在该IncrementTaskNumber方法中没有锁定机制。除非你只从一个地方调用它,否则它不是线程安全的。你想做的第一个实现,即使它会工作也不会,因为Interlocked.Increment(ref _TaskNumber);可能在第一个实现之前第二次被调用,并写入 ref 参数。

编辑:如果你想让它成为线程安全的,你可以像这样修改 yoru 方法:

private void IncrementTaskNumber()
{
    lock (_TaskNumber)
        _TaskNumber++;
}

编辑 2:(如果使用lock对您的应用程序来说成本太高,您可能需要研究其他解决方案。)

于 2012-08-27T16:23:18.120 回答
0

这绝对不是线程安全的:

private void IncrementTaskNumber() {
   int _taskNum = _TaskNumber;
   Interlocked.Increment(ref _taskNum);
   _TaskNumber = _taskNum;
}

因为执行序列的线程可能在这 3 个操作之间被中断。这将不起作用,除非您引入一个循环,如果同时本地值发生更改,该循环会不断重试递增,但这基本上意味着您正在重新实现Interlocked.Incrementusing Interlocked.Increment。:)

只需创建_TaskNumber一个成员变量,而不是属性。

于 2012-08-27T16:42:57.183 回答