gpt4 book ai didi

c# - 惰性共享异步资源——澄清一下?

转载 作者:太空狗 更新时间:2023-10-29 20:23:36 24 4
gpt4 key购买 nike

我在 Stephen 的书的末尾看到了这个例子。

这段代码可以被多个线程访问。

static int _simpleValue;
static readonly Lazy<Task<int>> MySharedAsyncInteger = new Lazy<Task<int>>(
async () =>
{
await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);
return _simpleValue++;
});

async Task GetSharedIntegerAsync()
{
int sharedValue = await MySharedAsyncInteger.Value;
}

无论有多少部分代码同时调用Value, Task<int>仅创建一次并返回给所有调用者。

然后他说:

If there are different thread types that may call Value (e.g., a UI thread and a thread-pool thread, or two different ASP.NET request threads), then it may be better to always execute the asynchronous delegate on a thread-pool thread.

因此他建议使用以下代码,使整个代码在线程池线程中运行:

static readonly Lazy<Task<int>> MySharedAsyncInteger = new Lazy<Task<int>>(() => Task.Run(
async () =>
{
await Task.Delay(TimeSpan.FromSeconds(2));
return _simpleValue++;;
}));

问题:

我不明白第一个 代码有什么问题。延续将在线程池线程中执行(由于 ConfigureAwait ,我们不需要原始上下文)。

同样,来自任何线程的任何控制都将到达 await。 ,控制权将返回给调用者。

我看不出第二个代码试图解决什么额外风险。

我的意思是 - 第一个 代码中的“可能调用 Value 的不同线程类型 ”有什么问题?

最佳答案

what is the problem with "different thread types that may call Value" in the first code?

该代码没有任何错误。但是,假设您有一些 CPU 绑定(bind)工作以及 async初始化调用。例如这样想象:

static readonly Lazy<Task<int>> MySharedAsyncInteger = new Lazy<Task<int>>(
async () =>
{
int i = 0;
while (i < 5)
{
Thread.Sleep(500);
i++;
}

await Task.Delay(TimeSpan.FromSeconds(2));
return 0;
});

现在,您无需“防范”此类操作。我假设 Stephan 提到了 UI 线程,因为您不应该对其执行任何超过 50 毫秒的操作。您永远不希望 UI 线程卡住。

当您使用 Task.Run 时要调用委托(delegate),您需要将自己从可能将长时间运行的委托(delegate)传递给您的 Lazy<T> 的地方隐藏起来。 .

Stephan Toub 在 AsyncLazy 中谈到了这一点:

Here we have a new AsyncLazy<T> that derives from Lazy<Task<T>> and provides two constructors. Each of the constructors takes a function from the caller, just as does Lazy<T>. The first constructor, in fact, takes the same Func that Lazy<T>. Instead of passing that Func<T> directly down to the base constructor, however, we instead pass down a new Func<Task<T>> which simply uses StartNew to run the user-provided Func<T>. The second constructor is a bit more fancy. Rather than taking a Func<T>, it takes a Func<Task<T>>. With this function, we have two good options for how to deal with it. The first is simply to pass the function straight down to the base constructor, e.g:

public AsyncLazy(Func<Task<T>> taskFactory) : base(taskFactory) { }

That option works, but it means that when a user accesses the Value property of this instance, the taskFactory delegate will be invoked synchronously. That could be perfectly reasonable if the taskFactory delegate does very little work before returning the task instance. If, however, the taskFactory delegate does any non-negligable work, a call to Value would block until the call to taskFactory completes. To cover that case, the second approach is to run the taskFactory using Task.Factory.StartNew, i.e. to run the delegate itself asynchronously, just as with the first constructor, even though this delegate already returns a Task<T>.

关于c# - 惰性共享异步资源——澄清一下?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32679483/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com