Isaac Levin Isaac Levin - 3 months ago 49
C# Question

MultiThreading with While Loop

I have an Azure Worker Role that calls 4 different services, I want to be able to run each in it's own thread and when one completes, kick off another iteration of it. Since they all take different times to run, I do not want to await for all of them before I kick off another occurrence when one completes. I have this which calls them all in sequence

public class WorkerRole : RoleEntryPoint
{
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false);

public override void Run()
{
Trace.TraceInformation("Polling.Worker is running");
try
{
this.RunAsync(this.cancellationTokenSource.Token).Wait();
}
finally
{
this.runCompleteEvent.Set();
}
}

public override bool OnStart()
{
// Set the maximum number of concurrent connections
ServicePointManager.DefaultConnectionLimit = 12;

// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.

bool result = base.OnStart();

Trace.TraceInformation("Polling.Worker has been started");

return result;
}

public override void OnStop()
{
Trace.TraceInformation("Polling.Worker is stopping");

this.cancellationTokenSource.Cancel();
this.runCompleteEvent.WaitOne();

base.OnStop();

Trace.TraceInformation("Polling.Worker has stopped");
}

private async Task RunAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Run(() =>
{
Debug.WriteLine("Starting Reddit Service");
RedditService.GetObjects();
Debug.WriteLine("Completed Reddit Service");
});

await Task.Run(() =>
{
Debug.WriteLine("Starting TV Show Service");
TVShowTicketService.GetObjects();
Debug.WriteLine("Completed TV Show Service");
});

await Task.Run(() =>
{
Debug.WriteLine("Starting Play By Play Service");
PlayByPlayService.GetObjects();
Debug.WriteLine("Completed Play By Play Service");
});

await Task.Run(() =>
{
Debug.WriteLine("Starting Profile Service");
ProfileService.Main();
Debug.WriteLine("Completed Profile Service");
});

await Task.Delay(1000);
}
}
}


I know that I am awaiting each thread, and I want to be able basically have some while mechanism that just repeats each function once it is complete, without worrying about the other threads.

Answer

I'm not sure I'm really understanding the question, because it seems like the answer to the question I'm reading is too obvious. Here's what I'm proposing though:

private async Task RunAsync(CancellationToken cancellationToken)
{
    await Task.WhenAll(
        Task.Run(() =>
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                Debug.WriteLine("Starting Reddit Service");
                RedditService.GetObjects();
                Debug.WriteLine("Completed Reddit Service");
                await Task.Delay(1000);
            }
        }),

        Task.Run(() =>
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                Debug.WriteLine("Starting TV Show Service");
                TVShowTicketService.GetObjects();
                Debug.WriteLine("Completed TV Show Service");
                await Task.Delay(1000);
            }
        }),

        Task.Run(() =>
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                Debug.WriteLine("Starting Play By Play Service");
                PlayByPlayService.GetObjects();
                Debug.WriteLine("Completed Play By Play Service");
                await Task.Delay(1000);
            }
        }),

        Task.Run(() =>
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                Debug.WriteLine("Starting Profile Service");
                ProfileService.Main();
                Debug.WriteLine("Completed Profile Service");
                await Task.Delay(1000);
            }
        }));
}

You can improve readability and maintainability by encapsulating the repeated elements in a helper method:

private async Task RunAsync(CancellationToken cancellationToken)
{
    await Task.WhenAll(
        RunServiceAsync(cancellationToken, RedditService.GetObjects, "Reddit"),
        RunServiceAsync(cancellationToken, TVShowTicketService.GetObjects, "TV Show"),
        RunServiceAsync(cancellationToken, PlayByPlayService.GetObjects, "Play By Play"),
        RunServiceAsync(cancellationToken, ProfileService.Main, "Profile"));
}

Task RunServiceAsync(CancellationToken cancellationToken, Action service, string description)
{
    return Task.Run(() =>
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            Debug.WriteLine("Starting " + description + " Service");
            service();
            Debug.WriteLine("Completed " + description + " Service");
            await Task.Delay(1000);
        }
    });
}

Does that not work for you? If not, why not? Please edit the question to make clear why this approach isn't suitable.

(I guess if it is suitable, the above suffices as an answer :) ).

Comments