Click here to Skip to main content
15,884,074 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
OK, I think I know... the problem is I might be wrong.

In general, if a variable is guaranteed to be written to by only one thread, it can be read by others with no problem. Correct?

More specifically, I could use a ConcurrentDictionary, but don't think it is necessary. The idea is that a Windows Form will start a thread with a polling operation. The thread will be to keep the form responsive.
I want the Form to display the number of jobs found in the polling operation.
I plan to create a dictionary<string,string> in the Form with a few entries, say:
"continue","true" and "jobs", "0"
If a Stop button on the Form is clicked, the value of "continue" in the dictionary will be set to "false". The while loop in the thread will read that and know to stop. Before that though, each time it polls, it will change the value of "jobs" in the dictionary to a string representing the number of polls (not really, but that is a real simple example). Now back in the Windows Form, a timer will be running and it will be reading that "jobs" value in the dictionary and displaying it.

The point is that only one thread will ever update the value of a KeyValuePair<string,> in the dictionary. The other thread will read it, but only one thread will ever write to that pair. Is that going to be safe to do? It doesn't even matter if the value is correct. It will be the next time the loop/event occurs.

Now I think the answer is that it is safe to do that, but if not, how about just sending a string, that is two strings. If only one thread is ever going to write to that string after the thread starts, does it matter what thread reads it or when?

I could even use an Interlocked.Increment(), but the question is whether a normal variable is safe to sue if only one thread ever writes to it.

All descriptions of threading seem to be about complicated situations. This is just simple.

Thanks, Mike
Posted
Updated 31-Jul-15 5:34am
v4

1 solution

If one thread reads from a dictionary, and another writes to dictionary, this dictionary is required to be thread-safe. But you can use a collection which is not thread-safe, because you can sandwich both operations (or all other operations accessed from different threads of the same collection object) in a lock statement using the same lock object. It's better not to use an accessible object (such as collection, or any object hosting it) as a lock object, to prevent using it directly. To protect from such cases, this object should be private, not accessible by the calling code.

Theoretically speaking, this is not a strictly required rule, just one simple and reliable pattern. You can always invent more relaxed synchronization rule which sometime could improve some average throughput. For example, you can design a schema that grants access to a collection element in a mutually exclusive manner, but you can have the operations on an element object which are done in parallel when two elements are different. It is especially easy to do if the elements are reference-type objects.

The whole idea to use a dictionary by different thread might be considered questionable. More typically, blocking collections are used. For example, I cannot see how it helps to keep the application responsive; the usual approach is to Control's or Dispatcher's Invoke or BeginInvoke, accessing no data in the UI, using only the data passed through UI thread invocation. Also remember: best synchronization is no synchronization. Maybe you improve your application threading design.

Don't get me wrong: I'm not saying you should not do it; for more certain advice, more detailed understanding of what you are trying to achieve would be required.

—SA
 
Share this answer
 
v3
Comments
Michael Breeden 31-Jul-15 12:13pm    
The last point is the easiest to answer. This is being developed in a Windows Forms application, but the classes are structured so that it could run in a console or Windows Service. The classes that do the work, do not "talk back" to their parents at all, though the parent (form, console, service) can get or set values in the thread.

As I mentioned, I can use a the thread safe ConcurrentDictionary or even a couple of Interlocked.Increment operators and be thread safe instead of using locking, but I am really more curious about if there would be a problem using:
1. A normal dictionary if only one thread will ever write to a a keyValuePair in it, though another thread will be doing the read.
2. A normal property (string) if only one thread will ever write to it, though another thread will be reading it.

Thanks, Mike
Sergey Alexandrovich Kryukov 31-Jul-15 14:40pm    
All right. I did not get what "point" are you answering to in your first paragraph, but such applications are quite possible. I used to develop whole technology where the same exact application, without any modifications, can serve as a Windows Service or as an interactive application, depending on how you start it. Still, it's not clear how you design it and deal with threading, but the whole thing is quite doable.

1) You really cannot access the same collection without locking (ConcurrentDictionary simply uses locking or other mutual exclusion facility internally, and Interlocked, generally, does not really solve the problem).
2) In many cases, you can improve throughput in cases when only one thread is writing data, and other threads are only reading. In such cases, you can use the class System.Threading.ReaderWriterLockSlim. I illustrated its use in my article Wish You Were Here… Only Once.

Will you accept my answer formally now? In all cases, your follow-up questions will be welcome.

—SA
Michael Breeden 31-Jul-15 15:23pm    
I'm sorry, but I must be explaining my intent very badly and you seem to be solving the trivial problem I present with a sledge hammer. I think this question is a bit too simple minded for you.
How about this? Please use the numbers and tell me if they are correct or incorrect. You do seem to have a deep enough knowledge of this to know these fine points.
1. As far as I know, there is no inherent reason that a normal variable cannot be read/write by two threads as long as it is in the scope of both (parent and child thread...).
2. If two threads try to write to the same variable at the same time, there can be a problem of creating an indeterminate (random) value in the variable. There will be no exception though.
3. If two threads try to read from the same variable at the same time, there should be no problem and the correct value will be read.
4. Even if two threads try to write to the same variable, but at different times, there should be no problem, no exception and the value of the variable will be correct after the individual writes.
5. If one thread writes to a variable and another reads the variable, even at the same time, it will not lead to an exception or an indeterminate value. It will be the value of the variable either before or after the write.

Really, I'm trying to build a wagon, not a race car. It is not about data, it is about flags controlling application behavior. I'm just trying to find out some details about what some thread behavior would be without using locking. All I have to work with are very old memories from basic classes.
Sergey Alexandrovich Kryukov 31-Jul-15 17:39pm    
1) Strictly speaking, it's not exactly so. That's why you have Interlocked.
2) Right. I guess, you did not mean random, you mean few different values depending on the order of execution. In other words, this is a potential race condition.
3) Strictly speaking, it's not exactly so. But yes, exceptions are irrelevant to this.
4) So, but this is exactly why either Interlocked or mutual exclusion is used, otherwise what can guarantee that "different time"?
5) Right.
Now, the problem is: your mistake was to reduce everything to reading and writing a single variable. We probably have some understanding here. But this is wrong to jump with the same considerations to the access to a collection. Accessing a member of a collection is never a single step. You first get access to a member, obtain its address, them you get the reference (for a reference type), dereference it, and then work with the object.
—SA
Michael Breeden 31-Jul-15 18:24pm    
#2. Fascinating. Didn't know was race condition, but the memory is very old.
I'm going to mark as answered and I do thank you for your time and expertise. I think I got the understanding I was looking for. Do you recommend an article on Interlocked for #1? Thanks again, Mike

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900