Re: variable shared between threads
Quote:
Originally Posted by boudino
Code:
Model<int> m = new Model<int>(); // model is implemented the way I've used before
...
m.Value = 20;
// start other thread which is using m and can modify it
// do other job in this thread
int i = m.Value;
Yes, technically this is perfectly threadsafe on all 32bit and higher platforms as writing a 32bit value is an atomic operation. But that does *not* require any locking to be 'thread safe'. The write is 1 operation and the read is 1 operation, therefore it is impossible to corrupt the value if you have multiple threads updating at the same time. So whether or not you add a 'lock' around it or mark it as volatile, you add no extra safety.
As i've said several times already, arjay's method of 'thread safety' adds *no extra level of safety* to the code. It's useless. Let me illustrate with a concrete example:
Code:
public class Model
{
private int _Number;
private object _nameFirstLock = new object( );
public int Number
{
get { lock (_nameFirstLock) return _Number; }
set { lock (_nameFirstLock) _Number = value; }
}
}
.....
void Method()
{
Model m = new Model();
m.Number ++;
}
As you can see, the getter and the setter are both protected by locks, so you think "Oh wow, this is threadsafe". It's not.
In order to run the line, m.Number++, several operations are performed. In pseudo code:
int temp = m.Number;
temp = temp + 1;
m.Number = temp;
Suppose thread1 executes line 1 and line 2, then it gets interrupted by thread2. Thread2 then executes line 1, 2 then 3 and exits. Thread1 continues and puts what is now an invalid and *old* value of temp into number.
So assuming the initial value of m.Number was '0', the final value will be 1 as opposed to 2 despite the fact two threads ran and incremented the value. This is why that locking does not work.
This is a bit of a stupid example and i wouldn't be surprised if 'increment by 1' can be accomplished as a single atomic action. However the point is valid. That locking just doesn't work.
Re: variable shared between threads
Actually what I displayed is thread safe with regard to protecting the get/set operations within the Property.
But as you know m.Number++; would call the get and then set so these two operations could be interupted.
These are points to think about though. If I need to increment/decrement I probably would do something like
Code:
int IncrementCounter( )
{
lock( _counterLock )
{
return _counter++;
}
}
int DecrementCounter( )
{
lock( _counterLock )
{
return _counter--;
}
}
acting on the varible directly and exposing threadsafe methods for class or external use.
Actually I wouldn't write it that way at all ( or use m.Number++ ); instead I would use the Interlocked methods.
Code:
int IncrementCounter( )
{
return Interlocked.Increment( ref _counter );}
int DecrementCounter( )
{
return Interlocked.Decrement( ref _counter );
}
Re: variable shared between threads
Yes, both of those would work. However they are just very specific workarounds for the very general problem i was outlining. My point is still there, proper access control to a classes variables can rarely (if ever) be effectively and efficiently controlled from entirely within that class.
You can't make everything threadsafe, so you need effective techniques to manage thread safety and thats what i'm trying to show.
Re: variable shared between threads
I think it's a good idea to strive for thread safe encapsulation within a class and for the most part you can achieve it within the class. One exception however, would be when a class exposes an enumerator and you need to cycle through the enumerator items. Certainly some sort of external locking would be required in this case.
I believe an important goal is, whenever possible, attempt to contain the synchronization within the class so that users of the class need not concern themselves with the synchronization chores.
Re: variable shared between threads
As an aside, I am working on a project that uses a dll to interact with a piece of hardware.
The following is an excerpt from the published documentation regarding the dll's thread safety:
"XYZ DLL is now also thread-safe. This means multi-threaded applications can call the API from different threads. Applications can pass channel handles between threads to allow multiple threads to access the same channel. However, it is the application’s responsibility to ensure more than one thread does not use the same channel at the same time. This can be done by using the Win32 synchronization functions to protect the channel handle."
I had to laugh when I read that because obviously the dll isn't thread safe if you need to provide external synchronization.
The above dll is a Win32 dll. The company also offer a .net version; however this one didn't fair much better. Creating a connection to the hardware, pinging it, and disconnecting causes it to leak memory, leak threads, and leak handles. Not real impressive.
Re: variable shared between threads
Quote:
Originally Posted by Mutant_Fruit
As for using the volatile keyword, it doesn't make things thread-safe. Thead safety is a hell of a lot more than just protecting access to variables and marking them as volatile.
yes generally volatile doesn't make things thread-safe but in this specific scenario there is no need to synchronize/lock access to the flag variable since it is going to be atomic. so i thought a volatile read/write is enough.
Re: variable shared between threads
Quote:
Originally Posted by Thread1
yes generally volatile doesn't make things thread-safe but in this specific scenario there is no need to synchronize/lock access to the flag variable since it is going to be atomic. so i thought a volatile read/write is enough.
I don't believe 'volatile' makes anything atomic. What volatile does is stop the compiler from making certain optimisations about reordering memory loads/writes. However a 'volatile' write to a long will still be two operations on a 32bit system (2x32bit writes). So that definitely isn't atomic.
I'm not 100% sure of this, but i can't find any docs which contradict that, and the stuff i've read only seems to mention the stuff about the memory reorderings.
Re: variable shared between threads
Quote:
Originally Posted by Marco Leon
This thread must set a flag to indicate to a form that there are a new event in order to refresh a datagrid in such a way that the new record appears in screen.
Actually, you just alluded to a better solution.. Events..
Just provide an event on the code doing the reading, and make sure any subscribers to that event that are UI components, invoke on the UI thread
Re: variable shared between threads
Thanks cjard for your suggestion, I will to learn about events.
Re: variable shared between threads
Just be careful using events because you rarely have a guarantee that the event will fire on the main thead (unless you are the one writing the events or you can look at the source code to check). Therefore thread safety becomes an issue when using events.
Re: variable shared between threads
Although you may use the event method to signal another thread to respond, it in itself doesn't guarantee a resource is protected so it's a good idea to update a shared resource with a lock, unlock and then fire the event. The event waiting thread would lock the resource, read it, and then unlock.
Re: variable shared between threads
Quote:
Originally Posted by Mutant_Fruit
I don't believe 'volatile' makes anything atomic. What volatile does is stop the compiler from making certain optimisations about reordering memory loads/writes. However a 'volatile' write to a long will still be two operations on a 32bit system (2x32bit writes). So that definitely isn't atomic.
I'm not 100% sure of this, but i can't find any docs which contradict that, and the stuff i've read only seems to mention the stuff about the memory reorderings.
yes that is why the c# compiler will allow only certain types for volatile keyword (i.e. integral types/ native integer).
Re: variable shared between threads
Quote:
Originally Posted by Marco Leon
Thanks cjard for your suggestion, I will to learn about events.
or you can use the BackgroundWorker class. it can raise an event that runs on the context of the UI thread without the "Invoke/InvokeRequired".