6
\$\begingroup\$

The situation is that a certain asynchronous operation is called multiple times, which is basically to consolidate results. But I want to abort every time a new request comes in, which means that only the last call will run completely. Does this solution look reasonable?

I have tried replicating the process in a test program, where the for loop represents 'x' number of calls generated for consolidation. But because we only need to run calculation once, we can safely run it on the last call.

I am creating a new thread each time, and cancelling the previous ones if there are any. This seems to work, but is there a better way or can this be improved?

static void Main(string[] args)
{
    var listCancellationTokens = new List<CancellationTokenSource>();

    for (var i = 1; i <= 5; i++)
    {
        var cancelSource = new CancellationTokenSource();
        listCancellationTokens.ForEach(c => c.Cancel());

        CreateThread(cancelSource);

        listCancellationTokens.Add(cancelSource);
    }
}

private static void CreateThread(CancellationTokenSource cancellationTokenSource)
{
    new Thread(() =>
    {
        try
        {
            ConsolidateResults(cancellationTokenSource.Token);
        }
        catch
        {
            ;
        }
    }).Start();

}

private static void ConsolidateResults(CancellationToken cancelToken)
{
    UpdateValues();
    if (cancelToken.IsCancellationRequested)
    {
        return;
    }
    TurnOffEvents();
    if (cancelToken.IsCancellationRequested)
    {
        return;
    }
    GetMarginPercentage();

    if (cancelToken.IsCancellationRequested)
    {
        return;
    }
    TurnOnEvents();
}

//This is how it is done at the moment
private void Commit(bool adjustDiscount = false)
{
    lock (_updateRunnerLock)
    {
        _updateRunnerRemainingCycles = 40;
        if (_updateRunner == null)
        {
            _updateRunner = new Thread(UpdateRunner);
            _updateRunner.Start();
        }
    }
}

private void UpdateRunner()
{
    while (_updateRunnerRemainingCycles > 0)
    {
        Thread.Sleep(500);
        _updateRunnerRemainingCycles--;
    }
    lock (this)
    {
        if (_updateRunner != null)
        {
            _updateRunner = null;
            ConsolidateResults();
        }
    }
}
\$\endgroup\$
0
6
\$\begingroup\$

Basically it is in its current state senseless to have a

var listCancellationTokens = new List<CancellationTokenSource>();  

if all you want is one running thread at any time.

So what I suggest instead is to create one Task and if there comes the situation that this task should be canceled you should cancel the task and Wait until the task returned.

After the Task returned you then create the new Task.

private static Task task;
private static CancellationTokenSource cancelSource;
static void Main(string[] args)
{

    for (var i = 1; i <= 5; i++)
    {
        if(task != null && (!task.IsCompleted || !task.IsFaulted))
        {

            try 
            {
                cancelSource.Cancel();
                task.Wait(cancelSource.Token);
            }
            catch (OperationCanceledException ex) 
            { 
                //swallow because by using ThrowIfCancellationRequested() 
                //in combination with cancelSource.Cancel() an exception is thrown.
            }
        }
        cancelSource = new CancellationTokenSource();

        task = Task.Factory.StartNew(() => ConsolidateResults(cancelSource.Token));

    }
    Console.ReadLine();
}  

then you should change the ConsolidateResults() method to

private static void ConsolidateResults(CancellationToken cancelToken)
{
    cancelToken.ThrowIfCancellationRequested();

    UpdateValues();
    TurnOffEvents();
    GetMarginPercentage();
    TurnOnEvents();
}
\$\endgroup\$
3
  • \$\begingroup\$ Why not use Task.Run(Action, CancellationToken) instead of Task.Factory.StartNew? (+1 though) \$\endgroup\$ – RobH Sep 4 '15 at 14:08
  • \$\begingroup\$ @RobH on this machine I have only NET 4.0 :-( the other part is I don't know what the methods in the worker method do. See the remarks here msdn.microsoft.com/en-us/library/dd988458%28v=vs.110%29.aspx \$\endgroup\$ – Heslacher Sep 4 '15 at 14:22
  • \$\begingroup\$ I never knew that - very useful information! Thanks for pointing it out :) \$\endgroup\$ – RobH Sep 4 '15 at 14:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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