[.net] locking in c#

Started by
12 comments, last by Daniel Miller 18 years, 10 months ago
edit: I was doing everything all wrong, but I still do not understand how to protect a variable from being accesse in another thread. Could you explain how? [Edited by - Daniel Miller on June 3, 2005 12:41:01 PM]
Advertisement
edit: irrelavent now

[Edited by - Daniel Miller on June 3, 2005 2:07:47 PM]
Threading is a fairly large subject, here are some resources.

The easiest way to make a variable thread safe (if that is what you are asking) is to declare it with the volatile keyword.
Actually, I was looking for the Monitor class, which locks a variable for a specific thread. I was looking at it so wrong you couldn't even imagine (I was trying to use 'lock' on a variable).
Does Monitor grant exclusive access to an actual object on the heap?

edit: weird things are happenning when I try and use Monitor.Enter.

For one, when I try to lock a public field of an instance of a class, it highlights "for (int x = 0;" on the next line and then that a says that a 'System.ArgumentNullException' was thrown.

But that is only when I try to attach a monitor to the field itself. Attaching it to the entire object has no effect.


edit: Whenever I make a change, it works for the first run, then never works again.

edit2: It's now alternating between working and not working. When it doesn't work, the output is strangely uniform for something that should be somewhat random. It should be all '*' without any '#' (ignore the '%%%%%'), but when it doesn't work:

%%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% *%%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% #%%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% *%%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% *%%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% #%%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% *%%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% *%%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% #%%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% *%%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% *%%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% #%%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% *%%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% *%%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% #%%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% *%%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% *%%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% #%%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% * %%%%% * %%%%% # %%%%% *


[Edited by - Daniel Miller on June 3, 2005 1:18:33 PM]
I would like to chime in to prevent confusion and errors.

I don't know what turnpast means exactly with "make a variable thread-safe", but to my sense, putting the "volatile" keyword doesn't solve all problems with multithreading. It should not be confused with Java's "synchronized".

"volatile" is a lightweight way to make some code thread-safe, but you have to carefully understand what it does and what this implies. It doesn't solve all multithreading scenarioes, and that's why there's the lock semantic.

For example, defining x as a volatile int will not make "x += 3" thread-safe (because it's not atomic). The result could be incorrect, if another thread reads/writes x after the reading but before the writing.

A few pointers:
C# specs, section 10.4.3 volatile fields. Explains what a volatile field is, and gives a small example of how it can be used to make some thread-safe code.

System.Threading.Interlocked class. Offers some atomic primitives (increment, exchange, ...) that are generally implemented in a lightweight fashion (i.e. using atomic CPU instructions).

C# lock keyword. Explains what the lock instruction is, what it does, and some examples. Note that the suggestion about what to lock on aren't that good, and I suggest you to read: Choosing what to lock on also.

System.Threading namespace. Contains other threading related primitives for synchronization. Look at classes Interlocked, Monitor, Mutex, ReaderWriterLock, ...

I know this is a lot of materials, but making efficient multithreading code is no simple issue.

Good luck,
jods
Thank you, but I am trying to make a single object un accessable from other threads. I am having the problems I mentioned above when I do so (I'm using monitor).
Daniel, what about posting your code here ?

Monitor.Enter and Exit are used to build critical sections. That means section of code that are going to be executed by only one thread at a time. In C#, instead of calling Monitor directly, you can use the lock keyword. That's basically the same, but may be more readable. lock calls Monitor.Enter, add a try...finally block, and calls Monitor.Exit in the finally clause.

Enter is a blocking call that will "acquire" the object you pass to it. Which object you pass is totally irrelevant. Enter will block if another thread has already "acquired" the same object, until it releases it. A thread releases an object that it has acquired by calling Exit.

There is no way to grant "exclusive access" to an object in C#. You have to protect the sections of code that access this object instead (here encapsulation / thread-safe classes are an obvious benefit).
Fixed the formatting, I think.

Main.cs:

using System;using System.Threading;namespace LockThreadingTest{	public class MainClass	{		public static void Main()		{			Speaker speaker = new Speaker();			SpeakerUser user = new SpeakerUser(speaker);			Thread thread = new Thread(new ThreadStart(user.Run));			thread.Start();			Monitor.Enter(speaker);			for (int x = 0; x != 1000; ++x)			{				speaker.quote = "*";				Console.Write("%");				Console.Write("%");				Console.Write("%");				Console.Write("%");				Console.Write("%");				speaker.Speak();			}			Monitor.Exit(speaker);			thread.Abort();			thread.Join();			Console.Read();		}	}}


Speaker.cs
using System;namespace LockThreadingTest{	public class Speaker	{		public void Speak()		{			Console.Write(" " + quote + " ");		}		public void SpeakForever()		{			while(true)			{				Console.Write(" " + quote + " ");			}		}		public string quote;	}}


SpeakerUser.cs



[source  lang="c#"]using System;namespace LockThreadingTest{	public class SpeakerUser	{		public SpeakerUser(Speaker speaker)		{            this.speaker = speaker;		}		public void Run()		{			while (true)			{				speaker.quote = "#";			}		}		private Speaker speaker;	}}


That is the code as I have it.

I am trying to prevent speaker.quote from being over written in the other thread.
Maybe what I am trying to do is simply a matter of locking sections of code.

This topic is closed to new replies.

Advertisement