Isaac Levin
Isaac Levin

Reputation: 2899

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.

Upvotes: 1

Views: 2922

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70671

If I've understood correctly, all you need to do is move your while loop into each task:

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);
        }
    });
}

Upvotes: 1

Related Questions