是否有一种方法可以在c# TPL中为任务做一种TryStart(不加锁)?
本文关键字:一种 任务 TryStart 加锁 方法 是否 TPL | 更新日期: 2023-09-27 18:18:54
这样,如果Task还没有被其他线程启动,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
,因此它仍然是线程安全的?
如果我理解正确,您正在尝试惰性创建一些资源。你可以用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;
}
如果自动包含null
,则将task
设置为tcs.Task
,并返回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;
}
我会像@i3arnon说的那样使用AsyncLazy<T>
,或者暴露Task<Resource>
而不是同步阻塞它:
public Task<Resource> GetResourceAsync()
{
lock (syncObj)
{
if (task == null)
task = Task.Run(() => CreateResource);
}
return task;
}
通过这种方式,您可以让调用者决定是否要同步或异步等待结果。考虑到这一点,我可能会将其公开为同步操作,并让调用者决定是否应该在后台线程中执行:
private readonly Lazy<Resource> resource = new Lazy<Resource>(() => CreateResource(), true);
public Resource GetResource()
{
return resource.Value;
}