I'm writing an audio application that has multiple threads producing sound and one thread that mixes the sounds and sends them to the sound card. I've tried several ways of synchronizing threads the 'right' way including Signals and thread safe queues but they were all too slow. So now I use a bool for each producer to indicate whether its queue is full. It seems to work very well (5ms latency for 32 threads) but is it safe to do it this way?
class PlayThreadParameters
{
public Queue<Samples> queue;
public bool isOutputQueueFull;
}
The producers look like this:
public void PolyPlayThread(object o)
{
var playThreadParameters = (PlayThreadParameters)o;
while (isPlaying)
{
while (playThreadParameters.isOutputQueueFull)
{
if (!isPlaying)
return;
Thread.Sleep(1);
}
... //fill output queue
playThreadParameters.isOutputQueueFull = true;
}
}
The consumer looks like this (called from a separate thread by Naudio):
public override int Read(byte[] array, int offset, int count)
{
for (int v = 0; v < playThreadParameters.Length; v++)
while (!playThreadParameters[v].isOutputQueueFull)
{
if (!isPlaying)
return 0;
Thread.Sleep(1);
}
... //mix the samples from the outputqueues
for (int v = 0; v < playThreadParameters.Length; v++)
playThreadParameters[v].isOutputQueueFull =false;
return count;
}
As far as I know, the .NET memory model doesn't guarantee that the changes of a variable made in one thread will be visible in another thread. You need a memory barrier there. The simplest (though not the most efficient) way to organize that is by using lock
or Interlocked
methods.
By the way, busy waiting is not the best method to achieve your goal. Maybe you'd like to switch to the producer-consumer model with appropriate condition variable (Monitor
s in C# parlance) usage?
No it is not completely safe, but you might get lucky most of the time ;-) You should be using the Interlocked methods to access the bool.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With