如何同时构建一个字符串?

时间:2017-10-28 06:56:02

标签: c# async-await task

在下面给出的MWE(最小工作示例)中,我配置

  • Simulator.CpuBoundOperation("red", 150));在线程池线程中使用红色字体旋转150次。

  • Simulator.CpuBoundOperation("green", 550);使用绿色字体旋转550次。

我希望获得一个HTML输出,该输出由一串带有交替红绿数字的线程ID组成,因为它们同时运行。

不幸的是,输出显示了不需要的结果,其中所有绿色数字首先打印,然后是所有绿色数字。我试图增加迭代但它也不起作用。

如何解决此问题?

MWE

using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

static class Simulator
{
    private static readonly StringBuilder sb = new StringBuilder();

    static Simulator()
    {
        string begin = "<!DOCTYPE html><html><body style='width:500px;background-color:black;font-size:20pt'><p>";
        sb.Append(begin);
    }

    public static void CpuBoundOperation(string color, int n = 50)
    {
        for (long i = 0; i < n; i++)
            sb.Append($"<span style='color:{color}'>{Thread.CurrentThread.ManagedThreadId} </span>");
        // a white space before </span> is important
    }

    public static new string ToString()
    {
        sb.Append("</p></body></html>");
        return sb.ToString();
    }
}

class Test
{

    static async Task Main(string[] args)
    {
        await Work();
        File.WriteAllText("output.html", Simulator.ToString());
    }

    static async Task Work()
    {
        Task t = Task.Run(() => Simulator.CpuBoundOperation("red", 150));
        Simulator.CpuBoundOperation("green", 550);
        await t;
        Simulator.CpuBoundOperation("blue");
    }
}

enter image description here

1 个答案:

答案 0 :(得分:3)

在主线程完成绿色数字之前,您的操作(显然)没有足够的时间让后台线程运行。

坦率地说,你很幸运,所有发生的事情都是后台任务在主线程等待之前没有开始。 StringBuilder不是线程安全的,所以如果你已经并发执行,你就会损坏StringBuilder,最多会产生完全错误的输出,并导致程序崩溃最糟糕的。

不清楚为什么要将文本交错,但如果这是一个要求,则应该在单个线程中执行此操作。至少在示例程序中,还没有足够的工作来证明并发性。如果你真的想要保证并发性,你必须为主线程添加逻辑,直到后台线程才开始工作,但这有点反模式,因为它违背了主要目的。并发:最大化计算吞吐量。