因此,如果任务尚未由另一个线程启动,则任务将启动。
例如
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
,以便保持线程安全?
答案 0 :(得分:3)
如果我理解正确,你会试图懒洋洋地创造一些资源。您可以使用AsyncLazy
执行此操作。此外,由于您已经阻止了该任务,因此您只能使用常规Lazy
:
Lazy<Resource> _lazy = new Lazy<Resource>(CreateResource);
public Resource GetResource()
{
return _lazy.Value;
}
但要直接回答你的问题。如果您要删除锁定并且仍然可以在其他线程上运行CreateResource
,则可以使用TaskCompletionSource
和Interlocked.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;
}