-2

这是我正在运行的代码:

    [TestMethod]
    public async Task HelloTest()
    {
        List<int> hello = new List<int>();
       //await Task.WhenAll(Enumerable.Range(0, 2).Select(async x => await Say(hello)));
        await Say(hello);
        await Say(hello);
    }

    private static async Task Say(List<int> hello)
    {
        await Task.Delay(100);
        var rep = new Random().Next();
        hello.Add(rep);
    }

为什么按原样运行此代码会按预期工作并产生两个随机数,但使用注释代码总是会产生两个完全相同的数字?

4

2 回答 2

2

所以你在这里有几个问题。

首先,为什么您两次看到相同的值。那是最容易的。当您创建一个Random实例时,它会以当前时间为种子,但它使用的当前时间的精度相当低。如果您Random在 16 毫秒左右(这对于计算机来说是一个非常长的时间)内获得了两个新实例,您会从它们中看到相同的值。这就是你正在发生的事情。

通常解决方法只是共享一个Random实例,但问题是您的随机实例没有从同一个线程访问(可能,假设您没有SynchronizationContext指定),并且Random不是线程安全的. 您可以使用类似这样的方法来获取随机数:

public static class MyRandom
{
    private static object key = new object();
    private static Random random = new Random();
    public static int Next()
    {
        lock (key)
        {
            return random.Next();
        }
    }

    //TODO add other methods for other `Random` methods as needed
}

使用它,它将解决眼前的问题。

您遇到的另一个问题,尽管目前似乎并没有让您感到困扰,但您正在List从两个不同的任务中修改您的任务,可能在不同的线程中执行。你不应该那样做。在单线程环境中使用这样的方法已经够糟糕的做法了(因为您依赖副作用来完成工作),但在多线程环境中,这非常有问题,不仅仅是概念上的原因。相反,您应该让每个线程返回一个值,然后将所有这些值拉入调用方的集合中,如下所示:

public async Task HelloTest()
{
    var data = await Task.WhenAll(Say(), Say());
}

private static async Task<int> Say()
{
    await Task.Delay(100);
    return MyRandom.Next();
}

至于为什么这两个Say调用是并行运行而不是顺序运行的,这与在您的第二个代码片段中您实际上并没有在开始下一个任务之前等待一个任务完成的事实有关。

您传递给Select的方法是启动任务的方法,它不会阻塞,直到该任务完成后再开始下一个任务。你在这里的代码:

await Task.WhenAll(Enumerable.Range(0, 2).Select(async x => await Say(hello)));

与简单地拥有没有什么不同:

await Task.WhenAll(Enumerable.Range(0, 2).Select(x => Say(hello)));

拥有一个async除了等待一个方法调用之外什么都不做的方法与只拥有一个方法调用没有什么不同。这里发生的Select是调用Say,盯着任务,继续,统计下一个任务,然后WhenAll(异步)等待两个任务完成,然后继续。

于 2013-09-13T14:33:19.647 回答
0

注意:这是对原始问题的回答(问题已被更改)。

它们的运作方式相同。我运行了以下控制台应用程序并得到了两个版本的结果 0 1:

class Program
{
    static int m_nextNumber = 0;

    static void Main(string[] args)
    {
        var t1 = Version1();

        m_nextNumber = 0;
        var t2 = Version2();

        Task.WaitAll(t1, t2);
        Console.ReadKey();
    }

    static async Task Version1()
    {
        List<int> hello = new List<int>();
        await Say(hello);
        await Say(hello);
        PrintHello(hello);
    }

    static async Task Version2()
    {
        List<int> hello = new List<int>();
        await Task.WhenAll(Enumerable.Range(0, 2).Select(async x => await Say(hello)));
        PrintHello(hello);
    }

    static void PrintHello(List<int> hello)
    {
        foreach (var i in hello)
            Console.WriteLine(i);
    }

    static int GotANumber()
    {
        return m_nextNumber++;
    }

    static async Task Say(List<int> hello)
    {
        var rep = GotANumber();
        hello.Add(rep);
    }
}
于 2013-09-13T13:54:26.093 回答