We currently have an interface for an asynchronous video decoder that runs on its own thread. Basically you feed it some data and it'll eventually call you back on its thread through some events, like so (not real code):

interface IDecoder {
    void Decode(parameters);
    event FrameReady;
    event FrameDropped;
    // lots of other possibilities

It's a bit messy and I'd like to convert it to a C#5, async-based version, something like:

interface IDecoder {
    async Task<DecodeResult> DecodeAsync(parameters);

Where DecodeResult will have all the information that would have otherwise been transmitted via events, and the client code can be written cleanly with async/await rather than a mess of event handlers and thread dispatching.

It is my understanding that Tasks are scheduled on the ThreadPool by default. We want all Decode operations to happen sequentially on a single, decoder-specific thread. Can this be achieved using Tasks, if so, how? Are Tasks even appropriate for this use or is there a more obvious pattern I'm missing?

2 Answers 2


It is my understanding that Tasks are scheduled on the ThreadPool by default.

Tasks that are created using Task.Run() (or Task.Factory.StartNew(), in the common case) are. But this does not apply to Tasks that don't execute any code themselves, like the ones that automatically created from async methods, or the ones that are created using TaskCompletionSource.

So, what you want is certainly possible. How exactly to do it depends on what should DecodeAsync() do.

EDIT: Probably the simplest way to convert your current code to Tasks would be using TaskCompletionSource. Simply call TaskCompletionSource.SetResult() instead of invoking an event.

But another option might be something like this:

class Decoder : IDecoder
    // initialize with a completed Task
    Task previous = Task.FromResult(true);

    public Task<DecodeResult> DecodeAsync(parameters)
        var result = DecodeAsyncInternal(parameters);
        previous = result;
        return result;

    private async Task<DecodeResult> DecodeAsyncInternal(parameters)
        await previous;
        return await Task.Run(() => ActualDecode(parameters));

This won't guarantee that all decoding is done on the same thread, but it does guarantee that the jobs will be executed one after the other.

Also, this code is not thread-safe: it won't work correctly if call DecodeAsync() at the same time from multiple threads. If you need that, use a lock.

  • DecodeAsync should launch a CPU-intensive job asynchronously (i.e. decoding), and return the result of the operation. These jobs must be performed one after the other, in the order they were submitted. Do you need more detail than that?
    – Asik
    Mar 8, 2014 at 15:38
  • Hm, I'm not familiar with Tasks and async, but your code doesn't seem to work. DecodeAsyncInternal has no return statement; if I replace the last await with return, the compiler tells me the method should return DecodeResult rather than Task<DecodeResult>...
    – Asik
    Mar 8, 2014 at 16:33
  • @Asik Right, you need return await, fixed.
    – svick
    Mar 8, 2014 at 16:37
  • That looks like what I vaguely had in mind. I'll have to think about it, but thanks for the tips.
    – Asik
    Mar 8, 2014 at 16:44
  • Hey, I posted a follow-up question on stackoverflow, if you could take a look at it, that'd be cool, thanks in advance. stackoverflow.com/q/22273055/154766
    – Asik
    Mar 8, 2014 at 20:00

You could implement a custom TaskScheduler for the purpose of only feeding one thread with the work of tasks as you can see at MSDN - How to: Create a Task Scheduler That Limits Concurrency

In rare scenarios, you might be able to achieve a performance speedup by creating a custom task scheduler that is derived from the System.Threading.Tasks.TaskScheduler class. You can then specify this scheduler by supplying a ParallelOptions object to an overload of the For or ForEach method. When you use Task objects directly, you can specify the custom scheduler by using the TaskFactory constructor that takes a TaskScheduler as an input parameter, or by some other means such as TaskFactory.StartNew.

You can also use a custom scheduler to achieve functionality that the default scheduler does not provide, such as strict first-in, first-out (FIFO) execution order...


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.