如何删除池线程中的当前任务?

时间:2016-03-06 13:21:56

标签: c# .net multithreading

我在asp.net页面中有这个事件处理程序:

protected void SetDescPoint(object sender, EventArgs e)
{
    try
    {
        ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(foo));
    }
    catch (Exception)
    { }
}

private void foo(object a)
{
    try
    {
        System.Diagnostics.Debug.WriteLine("Start - " + DateTime.Now.ToString("h:mm:ss tt"));
        TimeSpan minutes = TimeSpan.FromMinutes(10);
        System.Threading.Thread.Sleep(minutes);
        string path = UniquePath();
        File.Delete(path);
        System.Diagnostics.Debug.WriteLine("Deleted - " + DateTime.Now.ToString("h:mm:ss tt"));
    }
    catch (Exception ex)
    {
        Debug.WriteLine("EXCEPTION - " + ex.Message);
    }
}

SetDescPoint是事件处理程序并且响应客户端事件而被触发。正如你可以看到函数foo具有Thread.Sleep(10分钟),当事件处理程序在小于10分钟的时间间隔内被触发时可能会出现这种情况,所以在这种情况下我需要删除池线程中的当前任务(foo())。

任何想法我该如何实现它?

2 个答案:

答案 0 :(得分:1)

重写您的代码以使用Task.Delay。这有两个好处:你不再持有一个线程(因为Task.Delay在内部使用一个定时器),你可以使用取消令牌来取消等待:

protected CancellationTokenSource CancellationToken { get; private set; }

protected void SetDescPoint(object sender, EventArgs e)
{
    try
    {
        Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);

        this.CancellationToken = new CancellationTokenSource();

        Task.Run(() => foo(this.CancellationToken.Token), this.CancellationToken.Token);
    }
    catch (Exception)
    { }
}

private async Task foo(CancellationToken token)
{
    try
    {
        System.Diagnostics.Debug.WriteLine("Start - " + DateTime.Now.ToString("h:mm:ss tt"));
        TimeSpan minutes = TimeSpan.FromMinutes(10);
        await Task.Delay(minutes, token);
        string path = UniquePath();
        File.Delete(path);
        System.Diagnostics.Debug.WriteLine("Deleted - " + DateTime.Now.ToString("h:mm:ss tt"));
    }
    catch (Exception ex)
    {
        Debug.WriteLine("EXCEPTION - " + ex.Message);
    }
}

每当您想取消任务时,只需致电CancellationToken.Cancel()

答案 1 :(得分:1)

处理此问题的一种相当简单的方法是跟踪您创建的每个路径CancellationTokenSource(我不确定是否有多条路径或简单路径,但以防万一),然后在事件再次发生时查找它。

将此与Task.Delay一起使用,以非阻塞的方式异步控制,可以达到你想要的效果:

private ConcurrentDictionary<string, CancellationTokenSource> pathsToTokens = 
                    new ConcurrentDictionary<string, CancellationTokenSource>();

protected async void SetDescPointAsync(object sender, EventArgs e)
{
    CancellationTokenSource existingTokenSource;
    var path = UniquePath();
    if (pathsToTokens.TryGetValue(path, out existingTokenSource))
    {
        existingTokenSource.Cancel();
    }

    var cancellationTokenSource = new CancellationTokenSource();
    pathsToTokens.AddOrUpdate(path, cancellationTokenSource, 
                             (pathToFile, token) => cancellationTokenSource);

    try
    {
        await Task.Delay(TimeSpan.FromMinutes(10), cancellationTokenSource.Token)
                  .ConfigureAwait(false);
    }
    catch (TaskCanceledException tce)
    {
        // Token was cancelled, do something?
    }

    Foo(path);
    pathsToTokens.TryRemove(path, out cancellationTokenSource);
}

private void Foo(string path)
{
    try
    {
        File.Delete(path);
        Debug.WriteLine("Deleted - " + DateTime.Now.ToString("h:mm:ss tt"));
    }
    catch (Exception ex)
    {
        Debug.WriteLine("EXCEPTION - " + ex.Message);
    }
}

此代码会发生的情况是,对于您创建的每个路径,您都会分配一个新的CancellationTokenSource。每次触发事件时,都会检查现有令牌。如果它到位,这意味着事件仍然没有完成,你想要取消它。然后,您异步等待所需的时间。请注意,Task.Delay包含在try-catch中,因为调用CancellationTokenSource.Cancel会导致它在完成后抛出异常。