繁体   English   中英

在扩展方法中使用await运算符时,是否需要考虑可能的重入编码问题?

[英]Do I need to consider possible re-entrant coding issues when using the await operator in an extension method?

我将在负载测试中使用此方法,这意味着可能会很快从不同的线程发生数千次调用。 我想知道我是否必须考虑在后续调用中会发生什么,其中创建了一个新的WebClient但是在之前的等待完成之前?

    public static async Task<string> SendRequest(this string url)
    {
        using (var wc = new WebClient())
        {
            var bytes = await wc.DownloadDataTaskAsync(url);
            using (var reader = new StreamReader(new MemoryStream(bytes)))
            {
                return await reader.ReadToEndAsync();
            }
        }
    }

我使用术语reentrant来描述这个方法将被一个或多个线程调用的事实。

因此,我们想知道在多线程上下文中使用此方法可能会出现哪些潜在问题,或者通过在具有多个线程的环境中进行单个调用,或者从一个或多个线程进行多个调用。

首先要看的是这种方法在外部暴露的是什么。 如果我们设计这种方法,我们可以控制它的作用,但不能控制调用者的作用。 我们需要假设任何人都可以对他们传递给我们方法的任何事情做任何事情,他们对返回的值做了什么,以及他们对调用类的类型/对象实例做了什么。 让我们依次看看这些中的每一个。

网址:

显然调用者可以传入无效的URL,但这不是特定于异步或多线程的问题。 他们不能用这个参数做任何其他事情。 在将字符串传递给我们之后,它们不能将字符串从另一个线程变异,因为string是不可变的(或者至少在外部是可观察的不可变的)。

返回值:

所以乍一看,这实际上似乎是一个问题。 我们正在返回一个对象实例(一个Task ); 我们正在编写的这个方法正在改变该对象(将其标记为故障,例外,完成),并且该方法的调用者也可能会突变它(添加延续)。 这个Task最终可以从多个不同的线程中进行变异也很合理(任务可以传递给任意数量的其他线程,可以通过添加延续来改变它,或者在我们改变它时读取值)。

幸运的是, Task专门设计用于支持所有这些情况,并且由于内部执行的同步,它将正常运行。 作为这种方法的作者,我们不需要关心谁为我们的任务添加了什么延续,从什么线程,不同的人是否同时添加它们,发生了什么顺序,是否在之前添加了延续或者在我们将任务标记为已完成或其中任何一项之后。 虽然任务可以在外部进行变异,甚至可以从其他线程进行变异,但是从这种方法来看,他们无法通过任何方式对我们进行观察。 同样,无论我们做什么,他们的继续都会发挥适当的作用。 任务标记为已完成后,它们的延续将始终触发一段时间,如果已完成,则立即触发。 它没有基于事件的模型在事件被触发后添加事件处理程序以指示完成的可能竞争条件。

最后,我们有类型/实例的状态。

这个很容易。 它是一个static方法,所以即使我们想要也没有我们可以访问的实例字段。 此方法也没有静态字段访问,因此在我们需要关注的方式之间没有共享状态。

除了字符串输入和任务输出之外,此方法使用的状态完全是在此方法之外永远不可访问的局部变量。 由于此方法在单个线程中执行所有操作(如果存在同步上下文,或者即使使用线程池线程也至少按顺序执行所有操作),我们不需要担心内部的任何线程问题,只有可能是调用者在外部发生。

如果您担心在之前的调用完成之前多次调用方法,那么主要关注的是访问字段。 如果该方法正在访问实例/静态字段,那么不仅要考虑使用任何给定输入状态调用的方法的含义,还要考虑其他方法是否同时访问这些字段时会发生什么。 由于我们没有访问,因此这种方法没有实际意义。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM