473,472 Members | 2,128 Online
Bytes | Software Development & Data Engineering Community
Create Post

Home Posts Topics Members FAQ

Interlocked.Add Not Thread Safe with Longs on a 32bit system

Below are three classes for a console application. If put into three
separate files, the sub main() will launch multiple threads adding and
removing the same value. At the end we expect the value for all
Balances to be 0. When using an Integer things work fine. LONGS do
not.

We are using the Interlocked methods. I believe the Interlocked.Add
method is not thread safe when using longs on 32bit systems.

We are aware of Interlocked.Read (new to dotnet 2.0) which is required
for longs on 32bit systems, but is the dotnet framework missing a trick
here as Microsoft have introduced a new method for reading but haven't
got the .Add method to work correctly.

Note: This works find on single core single procssors.
But does not on muli-core or dual-core or hyper-threaded pentium 4.

Option Explicit On

Option Strict On

Module Module1

Public Tellers(99) As Teller

Private Threads(99) As Threading.Thread

Sub Main()

For i As Integer = 0 To 99

Tellers(i) = New Teller

Next

Do

For i As Integer = 0 To 99

Dim t As New Threading.Thread(AddressOf
Tellers(i).DoWork)

Threads(i) = t

t.Start()

Next

For i As Integer = 0 To 99

Threads(i).Join()

Next

Dim B2 As Integer = BankInt.Balance

Console.WriteLine("Int Balance " & B2)

Dim B1 As Long = BankLong.Balance

Console.WriteLine("Long Balance " & B1)

Loop

End Sub

End Module



Option Explicit On

Option Strict On

Imports System.Threading

Public Class Teller

Public Sub DoWork()

For i As Integer = 1 To 100000

BankInt.Deposit(50)

BankInt.Withdraw(50)

BankLong.Deposit(50)

BankLong.Withdraw(50)

Next

End Sub

End Class

Option Explicit On

Option Strict On

Imports System.Threading

Public Class BankLong

Private Shared _balance As Long

Public Shared ReadOnly Property Balance() As Long

Get

Return Interlocked.Read(_balance)

End Get

End Property

Public Shared Sub Deposit(ByVal Amount As Long)

Interlocked.Add(_balance, Amount)

End Sub

Public Shared Sub Withdraw(ByVal Amount As Long)

Interlocked.Add(_balance, -Amount)

End Sub

End Class

Public Class BankInt

Private Shared _balance As Integer

Public Shared ReadOnly Property Balance() As Integer

Get

Return _balance

End Get

End Property

Public Shared Sub Deposit(ByVal Amount As Integer)

Interlocked.Add(_balance, Amount)

End Sub

Public Shared Sub Withdraw(ByVal Amount As Integer)

Interlocked.Add(_balance, -Amount)

End Sub

End Class

Dec 8 '06 #1
12 2012
David,

Interlocked.Add is thread-safe for Int64. So where's the problem? Take
a closer look at the Withdrawl method. You're doing a lock-free
negation of an Int64.

Brian

David wrote:
Below are three classes for a console application. If put into three
separate files, the sub main() will launch multiple threads adding and
removing the same value. At the end we expect the value for all
Balances to be 0. When using an Integer things work fine. LONGS do
not.

We are using the Interlocked methods. I believe the Interlocked.Add
method is not thread safe when using longs on 32bit systems.

We are aware of Interlocked.Read (new to dotnet 2.0) which is required
for longs on 32bit systems, but is the dotnet framework missing a trick
here as Microsoft have introduced a new method for reading but haven't
got the .Add method to work correctly.

Note: This works find on single core single procssors.
But does not on muli-core or dual-core or hyper-threaded pentium 4.

Option Explicit On

Option Strict On

Module Module1

Public Tellers(99) As Teller

Private Threads(99) As Threading.Thread

Sub Main()

For i As Integer = 0 To 99

Tellers(i) = New Teller

Next

Do

For i As Integer = 0 To 99

Dim t As New Threading.Thread(AddressOf
Tellers(i).DoWork)

Threads(i) = t

t.Start()

Next

For i As Integer = 0 To 99

Threads(i).Join()

Next

Dim B2 As Integer = BankInt.Balance

Console.WriteLine("Int Balance " & B2)

Dim B1 As Long = BankLong.Balance

Console.WriteLine("Long Balance " & B1)

Loop

End Sub

End Module



Option Explicit On

Option Strict On

Imports System.Threading

Public Class Teller

Public Sub DoWork()

For i As Integer = 1 To 100000

BankInt.Deposit(50)

BankInt.Withdraw(50)

BankLong.Deposit(50)

BankLong.Withdraw(50)

Next

End Sub

End Class

Option Explicit On

Option Strict On

Imports System.Threading

Public Class BankLong

Private Shared _balance As Long

Public Shared ReadOnly Property Balance() As Long

Get

Return Interlocked.Read(_balance)

End Get

End Property

Public Shared Sub Deposit(ByVal Amount As Long)

Interlocked.Add(_balance, Amount)

End Sub

Public Shared Sub Withdraw(ByVal Amount As Long)

Interlocked.Add(_balance, -Amount)

End Sub

End Class

Public Class BankInt

Private Shared _balance As Integer

Public Shared ReadOnly Property Balance() As Integer

Get

Return _balance

End Get

End Property

Public Shared Sub Deposit(ByVal Amount As Integer)

Interlocked.Add(_balance, Amount)

End Sub

Public Shared Sub Withdraw(ByVal Amount As Integer)

Interlocked.Add(_balance, -Amount)

End Sub

End Class
Dec 8 '06 #2

Brian Gideon wrote:
David,

Interlocked.Add is thread-safe for Int64. So where's the problem? Take
a closer look at the Withdrawl method. You're doing a lock-free
negation of an Int64.

Brian
Hmm...I believe I spoke too soon. I just noticed that Amount in the
Withdraw method is on the stack. I'll have to look at this in a little
more detail when I get time.

Dec 8 '06 #3
David,

I'm just not seeing a problem with the code. Well, except that you
really need a call Interlocked.Read in the BankInt.Balance property,
but that wouldn't cause the problem you're seeing. I'll try to
remember to run this code on my dual-core laptop (not with me at the
moment) which has VS 2005 on it.

What balance are seeing at the end with longs?

Brian

David wrote:
Below are three classes for a console application. If put into three
separate files, the sub main() will launch multiple threads adding and
removing the same value. At the end we expect the value for all
Balances to be 0. When using an Integer things work fine. LONGS do
not.

We are using the Interlocked methods. I believe the Interlocked.Add
method is not thread safe when using longs on 32bit systems.

We are aware of Interlocked.Read (new to dotnet 2.0) which is required
for longs on 32bit systems, but is the dotnet framework missing a trick
here as Microsoft have introduced a new method for reading but haven't
got the .Add method to work correctly.

Note: This works find on single core single procssors.
But does not on muli-core or dual-core or hyper-threaded pentium 4.

Option Explicit On

Option Strict On

Module Module1

Public Tellers(99) As Teller

Private Threads(99) As Threading.Thread

Sub Main()

For i As Integer = 0 To 99

Tellers(i) = New Teller

Next

Do

For i As Integer = 0 To 99

Dim t As New Threading.Thread(AddressOf
Tellers(i).DoWork)

Threads(i) = t

t.Start()

Next

For i As Integer = 0 To 99

Threads(i).Join()

Next

Dim B2 As Integer = BankInt.Balance

Console.WriteLine("Int Balance " & B2)

Dim B1 As Long = BankLong.Balance

Console.WriteLine("Long Balance " & B1)

Loop

End Sub

End Module



Option Explicit On

Option Strict On

Imports System.Threading

Public Class Teller

Public Sub DoWork()

For i As Integer = 1 To 100000

BankInt.Deposit(50)

BankInt.Withdraw(50)

BankLong.Deposit(50)

BankLong.Withdraw(50)

Next

End Sub

End Class

Option Explicit On

Option Strict On

Imports System.Threading

Public Class BankLong

Private Shared _balance As Long

Public Shared ReadOnly Property Balance() As Long

Get

Return Interlocked.Read(_balance)

End Get

End Property

Public Shared Sub Deposit(ByVal Amount As Long)

Interlocked.Add(_balance, Amount)

End Sub

Public Shared Sub Withdraw(ByVal Amount As Long)

Interlocked.Add(_balance, -Amount)

End Sub

End Class

Public Class BankInt

Private Shared _balance As Integer

Public Shared ReadOnly Property Balance() As Integer

Get

Return _balance

End Get

End Property

Public Shared Sub Deposit(ByVal Amount As Integer)

Interlocked.Add(_balance, Amount)

End Sub

Public Shared Sub Withdraw(ByVal Amount As Integer)

Interlocked.Add(_balance, -Amount)

End Sub

End Class
Dec 8 '06 #4
"David" <da****@mclernons.comwrote:
>We are using the Interlocked methods. I believe the Interlocked.Add
method is not thread safe when using longs on 32bit systems.
Nice find! I reproduced the same buggy behaviour in C# and C++/cli, so
I agree that it looks like a problem with the clr.

--
Lucian
Dec 9 '06 #5
Hi Brian,

The value of the balance (long version) varies. We are not using
Interlocked.Read in the BankInt.Balance property as Interlocked.Read as
it only supports longs.

Regards

David

Dec 11 '06 #6
David,

It's strange that Add(Int32), Add(Int64), and Read(Int64) exist, but
Read(Int32) does not. I realize Read(Int32) isn't required for
atomicity's sake, but some kind of volatile read is so why not make the
API more consistent and add Read(Int32) that just calls
Thread.VolatileRead and call it good. Oh well, just use
Thread.VolatileRead instead. That will ensure that the current thread
sees changes made by other threads.

Again, other than that I don't see anything else wrong with your code.
Unless I'm missing something obvious I think you may have found a bug.
I guess I was a little too naive which is why I jumped the gun in my
first post...sorry about that.

Brian

David wrote:
Hi Brian,

The value of the balance (long version) varies. We are not using
Interlocked.Read in the BankInt.Balance property as Interlocked.Read as
it only supports longs.

Regards

David
Dec 11 '06 #7
"David" <da****@mclernons.comwrote:
>We are using the Interlocked methods. I believe the Interlocked.Add
method is not thread safe when using longs on 32bit systems.
Okay, I've filed it with the System.Threading team at microsoft, who
agree it looks like a bug as you say.

I was wondering about possible workarounds. On a 32bit machine, the
64bit Interlocked.Add is inevitably going to be implemented with a
busy-loop involving two interlocked operations anyway. So if you
wanted to write a fixed version of Interlocked.Add(Int64) yourself,
it'd look a bit like the following (untested):

Function MyInterlockedAdd(ByVal x As Long, ByVal i As Long)
retry:
Dim oldval As Long = Interlocked.Read(x)
Dim newval As Long = oldval + i
Dim fresholdval = Interlocked.CompareExchange(x, newval, oldval)
If fresholdval <oldval Then GoTo retry
Return newval
End Function

--
Lucian.
Dec 11 '06 #8
Hi Lucian,

Nice idea with the Interlocked.CompareExchange. Should be able to get
the time to test this in a day or so.

I'll keep you posted

David

Dec 13 '06 #9

Lucian Wischik wrote:
Function MyInterlockedAdd(ByVal x As Long, ByVal i As Long)
retry:
Dim oldval As Long = Interlocked.Read(x)
Dim newval As Long = oldval + i
Dim fresholdval = Interlocked.CompareExchange(x, newval, oldval)
If fresholdval <oldval Then GoTo retry
Return newval
End Function

The variable x should be passed ByRef. Also, make sure to test i = 0
to prevent an infinite loop.

Dec 13 '06 #10
"Brian Gideon" <br*********@yahoo.comwrote:
>Lucian Wischik wrote:
>Function MyInterlockedAdd(ByVal x As Long, ByVal i As Long)
retry:
Dim oldval As Long = Interlocked.Read(x)
Dim newval As Long = oldval + i
Dim fresholdval = Interlocked.CompareExchange(x, newval, oldval)
If fresholdval <oldval Then GoTo retry
Return newval
End Function

The variable x should be passed ByRef. Also, make sure to test i = 0
to prevent an infinite loop.
Nice catch on the ByRef. But I don't think i=0 will cause an infinite
loop?

PS. I'd written "the solution inevitably involves two interlocked
operations". Just goes to show how bad I am at multithreaded
programming. The .net guy who's just fixed the bug came up with a
solution that involves only one interlocked read, a solution that's 1
byte smaller codesize than the current Interlocked.Add(Int64), that
saves multiple memory-reads, and that is at last correct!

It goes something like this (again, completely untested, because I'm
coming up with VB on the code while the .net person wrote in
assembler):

Dim val as Long = x
retry:
Dim oldval as Long = val
Dim newval as Long = oldval+i
val = Interlocked.CompareExchange(x,newval,oldval)
if val<>oldval Then GoTo retry
Return val
The idea here is this:

(1) We're just reading "x" on its own, without an Interlocked.Read.
This is fine. Even if someone came along and modified x while we were
half way through reading it, and we end up with an inconsistent "val",
this will be picked up by the CompareExchange statement. The ABA
problem of nonblocking data structures doesn't matter here because
we're doing arithmetic, not reusing pointers.

(2) Within the loop we don't need an explicit statement to re-read
"x". That's because Interlocked.CompareExchange will return the old
value of "x" anyway.
--
Lucian
Dec 14 '06 #11

Lucian Wischik wrote:
Nice catch on the ByRef. But I don't think i=0 will cause an infinite
loop?
Ah...yeah, you're right. I have no idea what I was thinking.
>
PS. I'd written "the solution inevitably involves two interlocked
operations". Just goes to show how bad I am at multithreaded
programming. The .net guy who's just fixed the bug came up with a
solution that involves only one interlocked read, a solution that's 1
byte smaller codesize than the current Interlocked.Add(Int64), that
saves multiple memory-reads, and that is at last correct!
Well, I missed it too so don't feel bad. Multithreaded programming is
insanely difficult. The added complexity of lock-free strategies makes
it seem down-right impossible.

I've seen worse bugs, but don't you think it's strange that a bug like
that made it into a release? That's why I was so quick to the blame
the OP's code at first.

Dec 14 '06 #12
Lucian

We have tried the Interlocked.CompareExchange solution which works
fine. Many thanks for the idea.

If you get any feed back from the System.Threading team at microsoft I
would be very interested to see what they say.

Public Class MyInterlocked
Public Shared Function Add(ByRef x As Long, ByVal y As Long) As
Long
Dim val As Long = x
retry:
Dim oldVal As Long = val
Dim newVal As Long = oldVal + x
val = Interlocked.CompareExchange(x, newVal, oldVal)
If val <oldVal Then GoTo retry

Return val

End Function
End Class

Dec 15 '06 #13

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: n_o_s_p_a__m | last post by:
Hi, just a quick question: I have 2 objects running in their own threads (let's call them a & b). They both hold a reference to a common object running in a third thread (call it c) which has...
19
by: steve | last post by:
// What I want to do Use enumerated types with the Interlocked.Exchange methods Suggestions please // My estimation of problem Seems like Interlocked.Exchange only works with ints,...
3
by: George Ter-Saakov | last post by:
What is the purpose of having Interlocked.Increment if it does not work with variable declared as volatile. Here is my problem, Interlocked.Increment increments the variable in thread safe...
1
by: Eduardo Garcia-Prieto | last post by:
I have come accross a problem in using the Interlocked.Exchange(Object, Object) method while using Option Strict On in my project. I have a private class structure variable which can be updated...
3
by: Ryan Liu | last post by:
Hi, Can some one tell the criteria I can used to decide use of Interlocked.Increment() vs volatile , which is better? Thanks a lot! Ryan
12
by: David | last post by:
Below are three classes for a console application. If put into three separate files, the sub main() will launch multiple threads adding and removing the same value. At the end we expect the value...
29
by: NvrBst | last post by:
I've read a bit online seeing that two writes are not safe, which I understand, but would 1 thread push()'ing and 1 thread pop()'ing be thread-safe? Basically my situation is the follows: ...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
1
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
0
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated ...
0
muto222
php
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.