Parallel.ForEach using Thread.Sleep equivalent

后端 未结 2 669
孤独总比滥情好
孤独总比滥情好 2021-01-18 07:40

So here\'s the situation: I need to make a call to a web site that starts a search. This search continues for an unknown amount of time, and the only way I know if the searc

2条回答
  •  一个人的身影
    2021-01-18 08:06

    You can also write a generic function using TaskCompletionSource and Threading.Timer to return a Task that becomes complete once a specified retry function succeeds.

    public static Task RetryAsync(Func retryFunc, TimeSpan retryInterval)
    {
        return RetryAsync(retryFunc, retryInterval, CancellationToken.None);
    }
    
    public static Task RetryAsync(Func retryFunc, TimeSpan retryInterval, CancellationToken cancellationToken)
    {
        var tcs = new TaskCompletionSource();
    
        cancellationToken.Register(() => tcs.TrySetCanceled());
    
        var timer = new Timer((state) =>
        {
            var taskCompletionSource = (TaskCompletionSource) state;
    
            try
            {                   
                if (retryFunc())
                {
                    taskCompletionSource.TrySetResult(null);
                }
            }
            catch (Exception ex)
            {
                taskCompletionSource.TrySetException(ex);
            }
        }, tcs, TimeSpan.FromMilliseconds(0), retryInterval);
    
        // Once the task is complete, dispose of the timer so it doesn't keep firing. Also captures the timer
        // in a closure so it does not get disposed.
        tcs.Task.ContinueWith(t => timer.Dispose(),
                              CancellationToken.None,
                              TaskContinuationOptions.ExecuteSynchronously,
                              TaskScheduler.Default);
    
        return tcs.Task;
    }
    
    
    

    You can then use RetryAsync like this:

    var searchTasks = new List();
    
    searchTasks.AddRange(items.Select(
            downloadItem => RetryAsync( () => isSearchFinished(downloadItem),  TimeSpan.FromSeconds(2))  // retry timout
            .ContinueWith(t => downloadData(downloadItem), 
                          CancellationToken.None, 
                          TaskContinuationOptions.OnlyOnRanToCompletion, 
                          TaskScheduler.Default)));
    
    await Task.WhenAll(searchTasks.ToArray());
    

    The ContinueWith part specifies what you do once the task has completed successfully. In this case it will run your downloadData method on a thread pool thread because we specified TaskScheduler.Default and the continuation will only execute if the task ran to completion, i.e. it was not canceled and no exception was thrown.

    提交回复
    热议问题