异步方法中的 StringBuilder

3

如果我发起多个异步网络请求,将结果追加到全局变量中,比如一个 StringBuilder 中,这样做是否安全?我知道返回结果的顺序是不确定的。

  1. 这样做会导致很多任务阻塞吗?

  2. 这样做是安全的吗?

private static StringBuilder sb = new StringBuilder();

private static async Task AccessTheWebAsync()
{
    HttpClient client = new HttpClient();
    HttpResponseMessage response = await client.GetAsync(@"http://www.google.com/");
    sb.Append(response.StatusCode).Append(Environment.NewLine);        
}

static void Main(string[] args)
{
    List<Task> tasks = new List<Task>();
    for (int i = 0; i < 10; i++)
        tasks.Add(AccessTheWebAsync());
    Task.WaitAll(tasks.ToArray());
    Console.Write(sb.ToString());
    Console.ReadLine();
}
1个回答

5

这会导致很多任务阻塞吗?

不会。你需要等待所有任务完成,但这是有意设计的。否则,这是一种相当有效的编写方式。

这是安全的吗?

这是不安全的。如StringBuilder所述:“任何实例成员都不能保证线程安全。”(在这种情况下,Append是一个实例方法。)

由于您正在控制台应用程序中运行此代码,因此Append调用将在单独的线程上发生。您需要某种形式的同步,例如在Append调用周围使用lock语句。

请注意,如果您在Windows Forms或WPF应用程序中使用相同的代码,则在await之后运行的代码将使用初始SynchronizationContext进行调度,这将导致它始终在UI线程上运行,因此您不会遇到线程同步问题。但是,由于它是在控制台应用程序中,因此连续体将在单独的线程上运行。


如果你愿意的话,你可以为你的控制台应用程序添加同步上下文。 - Servy
1
这更像是一个后续问题。也许更合理的做法是将签名设置为private static async Task<string> AccessTheWebAsync(),返回response.StatusCode,然后在WaitAll循环结束后遍历并附加所有的Result - user17753
@user17753 是的,实际上那样可能会更好。 - Servy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接