Reputation: 1288
For now or since I learn to program, I'm using following codes to do this:
ConcurrentQueue<MyPkg> messageQueue= new ConcurrentQueue<MyPkg>;
private void Post()
{
while (true)
{
if (messageQueue.IsEmpty)
{
//Post to server
}
Thread.Sleep(1);
}
}
This while-sleep "Query Mode" looks so stupid....Most time the cpu just keep context switching for nothing.But many people are doing this. Is there a better way? A "interrupt" or "trigger" like way?
Upvotes: 2
Views: 1119
Reputation: 14007
There are many different ways to solve your problem. Which one is best depends on your situation. It is impossible to provide all the possible options here, especially since some of the solutions can become quite complex. At the end it is about knowing your options and using them as building blocks in your solution.
Here are some things you could do. Note that these are very basic implemenations that do not take into account cancellation and timeouts. You would have to add them to the mix depending on your requirements.
Spin wait
You can use SpinWait
to wait until your queue has any items. SpinWait
is a good choice when you expect the wait times to be rather short, because in these cases it would spin instead of causing a thread context switch. SpinWait
automatically locks if the waiting time gets too long, but still you shouldn't use it for longer waits.
An example would be:
while (true)
{
while (!messageQueue.IsEmpty)
{
//Post to server
}
SpinWait.SpinUntil(() => !messageQueue.IsEmpty);
}
Wait handle
Another option is to use a wait handle that can be set when an item is enqueued and that blocks the receiving thread. This will usually cause a thread switch, so this is a good option when data is coming in not too frequently, but processing is quite expensive or other threads might make better use of the processing time while there is nothing in the queue.
A good option for a wait handle would be an AutoResetEvent
:
AutoResetEvent waitHandle = new AutoResetEvent(false);
while (true)
{
waitHandle.WaitOne();
while (!messageQueue.IsEmpty)
{
//Post to server
}
}
//When you enqueue your items...
messageQueue.Enqueue(item);
waitHandle.Set();
Starting a task to process the queue
If the data in the queue comes in in short bursts followed by longer breaks, it can be best to not run anything in the background when there is nothing to do and only start a task that will empty the queue when there are items in it. In the longer breaks you can return the thread that will process the queue to the thread pool, where it can be used for something more important.
You can start a Task
on demand and use a bool
variable to track whether your processing task is running.
bool isProcessing = false;
object processingSync = new object();
//...
void ProcessQueue()
{
bool shouldContinue;
do
{
object item; //Set thhis to the right type
lock (processingSync)
{
//Note: you wouldn't actually need a concurrent queue here,
//since you are locking during enqueue and dequeue
shouldContinue = messageQueue.TryDequeue(out item);
if (!shouldContinue)
{
isProcessing = false;
}
}
//Process item
}
while (shouldContinue);
}
//When you enqueue your items...
lock (processingSync)
{
messageQueue.Enqueue(item);
if (!isProcessing)
{
Task.Run(ProcessQueue);
isProcessing = true;
}
}
When you know in advance how many items are in the queue, you could also use a BlockingCollection
, which has the advantage that you don't have to do your own manual locking.
Upvotes: 2