SpinWait.SpinUntil and the Task Parallel Library

Blocking executing code and periodically checking some value before proceeding is commonly done in asynchronous and parallel programming. Often blocking and checking is combined with some sort of timeout. SpinUntil, a static method on the SpinWait structure; combines blocking, waiting, and timeouts. It's new in the .NET Framework 4.0. SpinUntil simplifies many different scenarios involving the Task Parallel Library (TPL). The paragraphs below are some common recipes employing SpinUntil alongside TPL.

SpinUntil Overview

Windows allocates CPU time slices to its executing threads scattered among all of its processes. A thread is given a time slice on the CPU before Windows switches to the next available thread. The time slice length depends on the hardware, but, according to documentation it is somewhere in the 5 millisecond range.

SpinUntil runs a Boolean function and then "Yields" the running thread. Yielding instructs the operating system that the running thread will "give up" the remainder of its timeslice, allowing the operating system to move onto another available thread. The SpinWait.SpinUntil method is demonstrated in the following code.

SpinWait.SpinUntil(() =>
    {
        return true;
    }
);

SpinUntil will continue to block until the Func<bool> parameter returns true. The examples in this article utilize llamda expressions, but there is no reason why you could not use another method matching the Func<bool> signature. SpinUntil can also be configured to exit after an elapsed time in addition to checking the Func<bool> result. An example with the timeout parameter follows.

SpinWait.SpinUntil(() =>
{
    return false;
}
,new TimeSpan(0,0,0,0,500));
 

As stated earlier SpinUntil's capabilities complement features in many of the TPL data structures.

Non-consuming BlockingCollection

TPL BlockingCollections are useful in Producer/Consumer scenarios. A complete review of BlockingCollections is beyond the scope of this article, but "Introducing the .NET Framework 4.0 Task Parallel Library BlockingCollection" offers a good overview. In a Producer/Consumer scenario, a Producer Task adds data to the BlockingCollection and a Consumer Task pulls data from the Collection. BlockingCollection is optimized for the concurrency friendly behavior. So, for example, BlockingCollection minimizes the "locking" overhead often involved when a thread is adding to a data structure while another thread is removing from the same data structure.

Following is an example employing SpinWait.SpinUntil with the BlockingCollection.

var blockingCollection = new BlockingCollection<string>();

Console.WriteLine("Starting at " + DateTime.Now.ToString());

Task.Factory.StartNew(() =>
    {
        Thread.Sleep(2000);
        blockingCollection.Add(" and ran Take..");
    }
);

SpinWait.SpinUntil(() =>
    {
        return (blockingCollection.Count > 0);
    }
);

Task.Factory.StartNew(() =>
{
    Console.WriteLine("SpinUntil exited");
    Console.WriteLine(blockingCollection.Take());
    Console.WriteLine("Completed at " + DateTime.Now.ToString());
}
).Wait();

The code demonstrates how a program could wait until data appears in a BlockingCollection without, for example, using Take and therefore consuming data from the BlockingCollection. In the example, consumption is handled by another Task only after SpinUnitil has exited.

This code could have been written with a Task that blocks on the BlockingCollection. Consider, however, if an application wanted to wait for data in 100 BlockingCollections. Allocating a separate Task for each BlockingCollection could allocate more Tasks than are needed. Allocated blocked Tasks consume TPL and system resources.

Interlocked Checking

Something similar to waiting on BlockingCollection data can be performed on an Interlocked operation.B A complete review of the Interlocked class is beyond the scope of this article, but you can find the documentation at Microsoft's site. Incrementing or copying a variable may be one line of code, but underneath .NET it's a multi-step process. A Thread could be switched while in the middle of the copy operation. The Interlocked class ensures that, for example, incrementing a variable completes before Windows switches to another thread. Interlocked is a lower overhead alternative to a monitor (lock). Following is an Interlocked example utilizing SpinUntil.

int checkValue = 0;
 
Console.WriteLine("Starting at " + DateTime.Now.ToString());
 
Task.Factory.StartNew(() =>
{
    SpinWait.SpinUntil(() =>
        {
            return checkValue > 0;
        }
    , 200);
 
    Console.WriteLine("Exited from timeout " + checkValue.ToString() + " at " + DateTime.Now.ToString());
}
);
 
Task.Factory.StartNew(() =>
{
    SpinWait.SpinUntil(() =>
    {
        return checkValue > 0;
    }
    );
 
    Console.WriteLine("Exited from no timeout " + checkValue.ToString() + " at " + DateTime.Now.ToString());
}
);
 
var waitTask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(2000);
    Interlocked.Add(ref checkValue, 1);
}
);
waitTask.Wait();
Console.WriteLine("waitTask exited at " + DateTime.Now.ToString());
 

Aside from demonstrating Interlocked the example also passes a timeout parameter. Timeouts are useful when a Task may be performing many different operations within, for example, a while loop.

Cancellations

Cancellation Tokens include cancellation checking properties. "Understanding .NET Framework Task Parallel Library Cancellations" is a good Cancellation introduction. Cancelling running Tasks can be complicated. SpinUntil is an alternative to registering a method with the Cancellation or checking the Cancellation from within the Task. SpinUntil is also an alternative to a Continuation. So, for example, a developer could devote a special Task to monitoring a Cancellation and performing actions to exit the operations tethered to the Cancellation. A cancellation example follows.

var cancel = new CancellationTokenSource();
 
Console.WriteLine("Starting at " + DateTime.Now.ToString());
 
Task.Factory.StartNew(() =>
{
    SpinWait.SpinUntil(() =>
        {
            return cancel.Token.IsCancellationRequested;
        }
    );
    Console.WriteLine("Exited from IsCancellationRequested checking at " + DateTime.Now.ToString());
}
);
 

Task Result or Status Checking

Tasks have a Status Property. Like the other TPL data structures, Task properties are Concurrency friendly. You'll find an introduction to Tasks at "Understanding Tasks in .NET Framework 4.0 Task Parallel Library". The following SpinUntil example demonstrates checking a Task status.

var completion = new TaskCompletionSource<string>();
 
Console.WriteLine("Starting at " + DateTime.Now.ToString());
 
Task.Factory.StartNew(() =>
{
    SpinWait.SpinUntil(() =>
        {
            return completion.Task.IsCompleted;
        }
    );
    Console.WriteLine(completion.Task.Result);
    Console.WriteLine("Exited from TaskCompletion IsCompleted checking at " + DateTime.Now.ToString());
}
);
 
System.Threading.Timer timer = null;
timer = new Timer(new TimerCallback((obj) =>
    {
        Console.WriteLine("Timer executed at " + DateTime.Now.ToString());
        completion.SetResult("Completion done");
        timer.Dispose();
    }
),null,1000,Timeout.Infinite);
 
 

The example utilizes the TaskCompletionSource rather than allocating a Task. Again, SpinUntil can be an alternative to a Continuation. Task.Wait could have been utilized here. However, Wait only blocks until the underlying Task moves to completion. The example could have been extended to check a Task and some other data structure.

Conclusion

Operations on TPL Data Structures often involve blocking and blocking timeouts. SpinUntil, a static method on the SpinWait structure fills a gap in some common TPL blocking scenarios.



About the Author

Jeffrey Juday

Jeff is a software developer specializing in enterprise application integration solutions utilizing BizTalk, SharePoint, WCF, WF, and SQL Server. Jeff has been developing software with Microsoft tools for more than 15 years in a variety of industries including: military, manufacturing, financial services, management consulting, and computer security. Jeff is a Microsoft BizTalk MVP. Jeff spends his spare time with his wife Sherrill and daughter Alexandra.

Related Articles

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • In support of their business continuity and disaster recovery plans, many midsized companies endeavor to avoid putting all their eggs in one basket. Understanding the critical role of last-mile connectivity and always available Internet access for their enterprises, savvy firms utilize redundant connections from multiple service providers. Despite the good intentions, their Internet connectivity risk may still be in a single basket. That is because internet service providers (ISPs) and competitive local …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds