And*_*and 8 delphi multithreading thread-safety critical-section interlocked-increment
请原谅我略带幽默的头衔.我在其中使用了两个不同的"安全"一词(显然).
我对线程很陌生(好吧,我已经使用了多年的线程,但只有非常简单的形式).现在我面临着编写某些算法的parallal实现的挑战,并且线程需要处理相同的数据.考虑以下新手错误:
const
N = 2;
var
value: integer = 0;
function ThreadFunc(Parameter: Pointer): integer;
var
i: Integer;
begin
for i := 1 to 10000000 do
inc(value);
result := 0;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
threads: array[0..N - 1] of THandle;
i: Integer;
dummy: cardinal;
begin
for i := 0 to N - 1 do
threads[i] := BeginThread(nil, 0, @ThreadFunc, nil, 0, dummy);
if WaitForMultipleObjects(N, @threads[0], true, INFINITE) = WAIT_FAILED then
RaiseLastOSError;
ShowMessage(IntToStr(value));
end;
Run Code Online (Sandbox Code Playgroud)
初学者可能希望上面的代码显示消息20000000
.实际上,首先value
是等于0
,然后inc
是20000000
时间.但是,由于该inc
过程不是'原子',两个线程将发生冲突(我猜这inc
有三件事:它读取,它递增,并保存),因此很多inc
s将被有效"丢失".我从上面的代码中得到的典型值是10030423
.
最简单的解决方法是使用InterlockedIncrement
而不是Inc
(在这个愚蠢的例子中会慢很多,但这不是重点).另一种解决方法是将inc
内部置于一个关键部分(是的,在这个愚蠢的例子中也会非常慢).
现在,在大多数真实算法中,冲突并不常见.事实上,它们可能非常罕见.我的一个算法创建了DLA分形,我不时的变量之一inc
是吸附粒子的数量.这里的冲突是非常罕见的,更重要的是,我真的不在乎变量总和是20000000,2000000,20000319还是19999496.因此,它很容易不使用InterlockedIncrement
或关键部分,因为它们只是膨胀代码并使它(略微)慢到没有(据我所知)的好处.
但是,我的问题是:冲突的后果是否比增量变量的稍微"不正确"值更严重?例如,程序会崩溃吗?
诚然,这个问题可能看起来很可笑,因为,毕竟,使用的成本InterlockedIncrement
,而不是inc
是相当低(在很多情况下,但不是所有!),所以它是(也许)笨不是为了稳妥起见.但是我也觉得知道这在理论水平上是如何起作用会很好,所以我仍然认为这个问题非常有趣.