Home > Net >  Run ValueTasks on a custom thread pool
Run ValueTasks on a custom thread pool

Time:09-22

I am looking to execute a bunch of ValueTask-returning functions on a custom thread pool - i.e. on a bunch of threads I'm spawning and handling myself, rather than the default ThreadPool.

Meaning, all synchronous bits of these functions, including any potential task continuations, should be executed on my custom thread pool.

Conceptually, something like:

class Example
{
    async ValueTask DoStuff(int something)
    {
        // .. do some stuff in here, might complete synchronously or not, who knows ..
    }

    private void Test()
    {
        for (int i = 0; i < 1_000; i  )
        {
            Func<ValueTask> method = () => DoStuff(1);
            MyThreadPool.Queue(method);
        }
    }
}

What's the best way to do this?

My current approach is something like this:

class Example
{
    async ValueTask DoStuff(int something)
    {
        // .. do some stuff in here, might complete synchronously or not, who knows ..
    }

    private void Test()
    {
        SynchronizationContext myContext = new MyCustomThreadPoolSynchronisationContext();
        TaskScheduler myScheduler;
        
        var prevCtx = SynchronizationContext.Current;
        try
        {
            SynchronizationContext.SetSynchronizationContext(myContext);
            myScheduler = TaskScheduler.FromCurrentSynchronizationContext();
        }
        finally
        {
            SynchronizationContext.SetSynchronizationContext(prevCtx);
        }

        var myTaskFactory = new TaskFactory(myScheduler);
        
        
        for (int i = 0; i < 1_000; i  )
        {
            myTaskFactory.StartNew(() => DoStuff(i).AsTask());
        }
    }
}

This seems to work, but having to convert the ValueTask into a Task and lodging it to a TaskFactory feels exceptionally clunky. And having to install my synchronization context, just to be able to get a hold off an appropriate TaskScheduler (and then immediately falling back onto the old synchronization context) feels quite off too.

Are there any conceptual flaws with my current approach?

Better yet, are there any better, less awkward ways of doing this?

CodePudding user response:

Everything I've read about creating a custom ThreadPool says don't.

An alternative would be to use a custom TaskScheduler on the shared ThreadPool.

You can use this TaskScheduler class like this:

static async Task Main(string[] args)
{
    // Create a scheduler that uses four threads.
    LimitedConcurrencyLevelTaskScheduler lcts = new LimitedConcurrencyLevelTaskScheduler(4);
    List<Task> tasks = new List<Task>();

    TaskFactory factory = new TaskFactory(lcts);
    CancellationTokenSource cts = new CancellationTokenSource(10000);

    // Start 20 tasks that will run 4 threads at 100% CPU
    for (var i = 0; i < 20; i  )
        tasks.Add(factory.StartNew(() => {
            while (true)
                if (cts.Token.IsCancellationRequested)
                    break;
        },cts.Token));

    await Task.WhenAll(tasks);
    cts.Dispose();
}
  • Related