How to make sure that Task.Factory.StartNew does not slow down the main thread?


There's a bunch of compressed data chunks that has to be asynchronously deflated - without blocking or slowing down the main thread in any shape or form.

Decompressed chunks will be used by the main thread as soon as they are decompressed.

Currently I do it like this:

foreach (var chunkPair in compressedChunkData)
    var task = Task.Factory.StartNew<Chunk>(() =>
        var compressedBytes = Convert.FromBase64String(chunkPair.Value);
        var chunk = Decompress(compressedBytes);
        return chunk;
    }).ContinueWith((finishedTask) =>
        var chunk = finishedTask.Result;
        TaskFinishActions.Enqueue(() =>
            document.Chunks.Add(chunkPair.Key, chunk);
// By the time we get here 20ms has passed!!!

The problem is that this seems to hijack the core the mainthread is running on, this butchers the performance.

Is there a way to make TaskFactory have thread per core and context switch away from mainthread only in those brief moments when mainthread is blocked?

EDIT: the foreach loop is not the only part of the code which becomes slow, as long as there sizable amount of decompression tasks running, mainthread slows down significantly.

EDIT2: New data to decompress arrive all the time, the loop is not ran only once:

  • Lets say you have 250 items arriving in compressedChunkData first
  • Next frame you have 10 items, next 12, next 0, next 2, etc.

You could use a custom TaskScheduler that sets the thread priority to a low value. Windows always schedules higher priority threads first.

Maybe you need to put an expiration date on tasks so that they don't queue up too much. It sounds like you have a need for low-latency processing. Each task could check as its first action whether it was scheduled more than N seconds ago, and if yes exit immediately.

An alternative design would be a producer/consumer scenario with low-priority threads taking work. I see no need for this given your requirements but it's a more flexible solution. Creating hundreds of tasks is not a problem. Each task is just a small in-memory data structure. Task != threads.