views:

131

answers:

4

I'm writing an application with a layered communications interace. This was done to abstract the communications from the user-interface part of the application and also to make it more scaleable/maintainable. For instance:

alt text

Consider each box in the figure above as a separate class. The Generic Comms Interface populates string variables describing the transacted data and comms "health," which are in turn copied up to the Application through a series of public function calls. For example, the Application would make a call to the App-Sub-System:

class Application
{
   private void SomeUpdateFunction()
   {
      this.textBox1.AppendText(this.AppSubSystem.GetText());
   }
}

class AppSubSystem
{
   public string GetText()
   {
      return this.GenericCommsInterface.GetText();
   }
}

class GenericCommsInterface
{
   public string GetText()
   {
      string sRetVal = this.sText; // sText is populated by other functions in the class.
      this.sText = null; // Suspected race condition is here.
      return sRetVal;
   }
}

sText is populated asynchronously by other functions in the class. I belive a race condition is happening between string sRetVal = this.sText; and the following line this.sText = null;. Can someone suggest a way to avoid or prevent this race condition? Would using StringBuilder help, or is there another way that I should be doing this?

A: 

where are the threads. There are no races without threads

pm100
This is not entirely true :-) Multiple threads can greatly *increase* the chance of race conditions, however: Any two (or more) tasks that access the same mutable state can cause a race condition. Consider "non-threaded" async. frameworks or "non-threaded" interrupts. The difference is merely the size of the atomic blocks and if they are implicit or explicit.
pst
perhaps, but thats not the case in C# or the code shown. THere is no way anything can come between the 2 lines he mentions that would cause the data to be invalid.
pm100
+1  A: 
public string GetText()
{
   lock( someObject )
   {
      string sRetVal = this.sText; // sText is populated by other functions in the class.
      this.sText = null; // Suspected race condition is here.
      return sRetVal;
   }
}    

in your set

lock( someObject )
{
    //...
    this.sText = value;
}
BioBuckyBall
This will *NOT* do it--a new string could be written in after the read but before the null. The whole operation must be protected, not just the parts.
Loren Pechtel
interning will bite you in the nether regions.. locking on a string is worse than not locking at all.
Sky Sanders
I don't disagree, but I didn't define my lock object at all, let alone define it as string. I was assuming he could take the gist of the thing and fill in the blanks.
BioBuckyBall
A: 

This code most certainly won't work in a threaded environment as you aren't protecting sText. You need to lock everybody that accesses it.

Loren Pechtel
It'll work -- *sometimes*. Other times, messages will mysteriously disappear.
cHao
Working sometimes is not working.
Loren Pechtel
@Loren Pechtel I work 8 (ish) hours a day. Am I unemployed? :)
BioBuckyBall
+2  A: 

You should probably be acquiring a lock any time you want to touch this.sText -- in the functions that update it, as well as your GetText function. This would ensure that only one thread at a time is messing with it, as (assuming your thread has the lock) other threads will sit and wait til the current thread is done.

I'd recommend you use a StringBuilder, partly to simplify locking, as locking a string that happened to be interned, or one switched out in the middle of the locked operation (and thus unlocked, from an outsider's perspective) could cause real bad mojo. Something like this would help:

lock (this.sbText)
{
    sRetVal = this.sbText.ToString();
    this.sbText.Length = 0;
}

Alternatively, you could lock on this, but that's ugly -- your locks should be inside, as private as possible, in order to avoid weird side effects (like if some other object were trying to acquire a lock on this object -- it couldn't do so while sbText was being altered).

cHao
Very bad to 'lock' onto an object not guaranteed to be stable across the thread access -- locks are 'advisory' in that everyone must use/honor them. This is particularly bad because you keep changing the lock object (by setting the variable that contains it to null).
pst
Good point. Another reason to use a StringBuilder.
cHao
@cHao: Thanks for the tips! I replaced `private string sText` with `private StringBuilder cText` and implemented the locks. It works great! The race condition has dissapeared. Kudos!
Jim Fell