Skip to main content

OrderedThreadPool - Task Execution In Queued Order !!!

I would not want to write chunks of code to spawns threads and perform many of my background tasks such as firing events, UI update etc. Instead I would use the System.Threading.ThreadPool class which serves this purpose. And a programmer who knows to use this class for such cases would also be aware that the tasks queued to the thread pool are NOT dispatched in the order they are queued. They get dispatched for execution in a haphazard fashion.

In some situations, it is required that the tasks queued to the thread pool are dispatched (and executed) in the order they were queued. For instance, in my (and most?) applications, a series of events are fired to notify the clients with what is happening inside the (server) application. Although the events may be fired from any thread (asynchronous), I would want them or rather the client would be expecting that the events are received in a certain order, which aligns with the sequence of steps carried out inside the server application for the requested service. So sequential execution of the queued tasks is not something one must not wish for.

Enough talking.......eat code.

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace System.Threading
{
struct ThreadPoolTaskInfo
{
public readonly WaitCallback CallbackDelegate;
public readonly object State;

public ThreadPoolTaskInfo(WaitCallback wc, object state)
{
Debug.Assert(wc != null);
CallbackDelegate = wc;
State = state;
}
}

class OrderedThreadPool
{
private Queue workItemQ = new Queue();

public void QueueUserWorkItem(WaitCallback wcbDelegate, object state)
{
lock (workItemQ)
{
workItemQ.Enqueue(new ThreadPoolTaskInfo(wcbDelegate, state));

if (workItemQ.Count == 1)
{
ThreadPool.QueueUserWorkItem(LoopWork);
}
}
}

private void LoopWork(object notUsed)
{
WaitCallback wcb = null;
object state = null;

lock (workItemQ)
{
if (workItemQ.Count == 0)
{
return;
}

ThreadPoolTaskInfo tptInfo = workItemQ.Dequeue();
state = tptInfo.State;
wcb = tptInfo.CallbackDelegate;
Debug.Assert(wcb != null);
}

try
{
wcb(state);
}
finally
{
ThreadPool.QueueUserWorkItem(LoopWork, notUsed);
}
}
}
}

The above class wraps the System.Threading.ThreadPool and offers the facility of execution of tasks in the order they are queued. Hope that is useful!

Comments

Anonymous said…
Hi

I think there is a small window for error in the OrderedThreadPool class.

Bascially, if an item of work is queued, then a worker thread runs, takes the item off the queue and is about to call wcb(state) - but at that instant is (say) context switched.

Then another item gets queued and another worker thread runs and dequeues the item and then again is about to call wcb(state).

There is scope here for the two operations to run concurrently or even out of order...

Do you agree or am I missing something?

Hugh
Unknown said…
Hi Hugh

You are right. There is a window where the the execution could go out of order or concurrent.

I have fixed the issue here. Hope it really fixed the problem you pointed out.

Regards
Vivek Ragunathan

Popular posts from this blog

Extension Methods - A Polished C++ Feature !!!

Extension Method is an excellent feature in C# 3.0. It is a mechanism by which new methods can be exposed from an existing type (interface or class) without directly adding the method to the type. Why do we need extension methods anyway ? Ok, that is the big story of lamba and LINQ. But from a conceptual standpoint, the extension methods establish a mechanism to extend the public interface of a type. The compiler is smart enough to make the method a part of the public interface of the type. Yeah, that is what it does, and the intellisense is very cool in making us believe that. It is cleaner and easier (for the library developers and for us programmers even) to add extra functionality (methods) not provided in the type. That is the intent. And we know that was exercised extravagantly in LINQ. The IEnumerable was extended with a whole lot set of methods to aid the LINQ design. Remember the Where, Select etc methods on IEnumerable. An example code snippet is worth a thousand ...

Implementing COM OutOfProc Servers in C# .NET !!!

Had to implement our COM OOP Server project in .NET, and I found this solution from the internet after a great deal of search, but unfortunately the whole idea was ruled out, and we wrapped it as a .NET assembly. This is worth knowing. Step 1: Implement IClassFactory in a class in .NET. Use the following definition for IClassFactory. namespace COM { static class Guids { public const string IClassFactory = "00000001-0000-0000-C000-000000000046"; public const string IUnknown = "00000000-0000-0000-C000-000000000046"; } /// /// IClassFactory declaration /// [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid(COM.Guids.IClassFactory)] internal interface IClassFactory { [PreserveSig] int CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject); [PreserveSig] int LockServer(bool fLock); } } Step 2: [DllImport("ole32.dll")] private static extern int CoR...

Passing CComPtr By Value !!!

This is about a killer bug identified by our chief software engineer in our software. What was devised for ease of use and write smart code ended up in this killer defect due to improper perception. Ok, let us go! CComPtr is a template class in ATL designed to wrap the discrete functionality of COM object management - AddRef and Release. Technically it is a smart pointer for a COM object. void SomeMethod() { CComPtr siPtr; HRESULT hr = siPtr.CoCreateInstance(CLSID_SomeComponent); siPtr->MethodOne(20, L"Hello"); } Without CComPtr, the code wouldn't be as elegant as above. The code would be spilled with AddRef and Release. Besides, writing code to Release after use under any circumstance is either hard or ugly. CComPtr automatically takes care of releasing in its destructor just like std::auto_ptr . As a C++ programmer, we must be able to appreciate the inevitability of the destructor and its immense use in writing smart code. However there is a difference...