有没有办法为c#TPL(没有锁定)的任务做一种TryStart?

时间:2015-10-23 11:02:26

标签: c# .net locking task-parallel-library task

因此,如果任务尚未由另一个线程启动,则任务将启动。

例如

private readonly object syncObj = new object();
private Task<Resource> task;

public Resource GetResource()
{
    lock (syncObj)
    {                
        if (task == null)
            task = Task.Factory.StartNew<Resource>(CreateResource);
    }

    task.Wait();

    return task.Result;
}

private Resource CreateResource()
{
    //do something
}

有没有办法在不使用GetResource()的情况下重写lock,以便保持线程安全?

2 个答案:

答案 0 :(得分:3)

如果我理解正确,你会试图懒洋洋地创造一些资源。您可以使用AsyncLazy执行此操作。此外,由于您已经阻止了该任务,因此您只能使用常规Lazy

Lazy<Resource> _lazy = new Lazy<Resource>(CreateResource);

public Resource GetResource()
{
    return _lazy.Value;
}

但要直接回答你的问题。如果您要删除锁定并且仍然可以在其他线程上运行CreateResource,则可以使用TaskCompletionSourceInterlocked.CompareExchange来执行此操作:

public Resource GetResource()
{
    var tcs = new TaskCompletionSource<Resource>();
    var storedTask = Interlocked.CompareExchange(ref task, tcs.Task, null);
    if (storedTask != null)
    {
        return storedTask.Result;
    }

    var resource = Task.Factory.StartNew<Resource>(CreateResource).Result;
    tcs.SetResult(resource);
    return resource;
}

如果task原子地包含tcs.Task并且返回null的原始值,则仅将task设置为CreateResource。这样我们就可以第一次启动task == null任务(task),并使用结果完成存储的async Task<Resource> GetResourceAsync() { var tcs = new TaskCompletionSource<Resource>(); var storedTask = Interlocked.CompareExchange(ref task, tcs.Task, null); if (storedTask != null) { return await storedTask; } var resource = await Task.Run(() => CreateResource()); tcs.SetResult(resource); return resource; }

如果你使它变为异步而不是阻塞,这变得更加简单:

for(var i = 0 ; i < ids.length; i++){
     $http.get("api/update?Id=" + ids[i]).then(function (result) {
                            $scope.nodeUpdate = result;
    });
}

   $scope.$watch('nodeUpdate', function() {
      //update node result, the result will have node id+new data
   });

答案 1 :(得分:2)

我会使用AsyncLazy<T>作为@ i3arnon说,或者公开Task<Resource>而不是同步阻止它:

public Task<Resource> GetResourceAsync()
{
     lock (syncObj)
     {                
         if (task == null)
             task = Task.Run(() => CreateResource);
     }

     return task;
}

这样,您让调用者决定是否要同步或异步等待结果。

在考虑它时,我可能会expose this as a synchronous operation instead,让调用者决定是否应该在后台线程上执行:

private readonly Lazy<Resource> resource = new Lazy<Resource>(() => CreateResource(), true);
public Resource GetResource()
{
      return resource.Value;
}