簡體   English   中英

了解AsParallel和AsSequential:LINQ查詢的哪一部分是並發的?

[英]Understanding AsParallel and AsSequential: which part of the LINQ query is concurrent?

我試圖了解我是否可以使用AsParallel查詢留下的非線程安全類。 就像是:

src.Select(item => nonSafeClass.Process(item))
   .AsParallel()
   .Select(item => DoComputationalIntenseButThreadSafeWork(item));

我試圖運行以下代碼,以查看查詢鏈的哪個部分並行執行,而不是並行執行:

IEnumerable<int> array = Enumerable.Range(0, short.MaxValue).ToArray();
array.Select(i =>
    {
        Console.WriteLine("Step One: {0}", Thread.CurrentThread.ManagedThreadId);
        return i;
    }).AsParallel().Select(i =>
        {
            Console.WriteLine("Step Two: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;

        }).AsSequential().Select(i =>
            {
                Console.WriteLine("Step Three: {0}", Thread.CurrentThread.ManagedThreadId);
                return i;
            }).ToList();

但是令我驚訝的是,“第一步”和“第三步”都出現在不同的線程ID上。 我期望只為“第二步”看到不同的線程ID,因為它在AsParallelAsSequential 我的想法錯了嗎?

這是因為推遲執行

以及Linq中的鏈接查詢如何工作

如果您將其更改為最簡單的情況

array.Select(i =>
        {
            Console.WriteLine("Step One: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;
        }).Select(i =>
        {
            Console.WriteLine("Step Two: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;

        }).Select(i =>
        {
            Console.WriteLine("Step Three: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;
        }).ToList();

您將看到以下內容:步驟1步驟2步驟3步驟1步驟2步驟3 ... ...

現在想象一下您的假設是否正確:

首先Select()在Thread 1 (主線程)上運行。 然后,您的AsParallel在不同的線程上運行,但是最終,最終的AsSequential()需要在同一線程上運行,這意味着,由於Thread 1被阻塞,使AsParallel在不同的線程上運行沒有任何區別。

您正在考慮的流程為: 1 -> x -> 1

跟着1 -> y -> 1

等等等等。

作為一種優化,當Linq檢測到您有一個select之后是AsParallel ,它將在單獨的線程上為每次迭代運行它們。 同樣,這是因為從1 -> x -> 1 -> y不會使任何內容以“並行”方式運行。

通過運行簡化版本來嘗試:

array.Select(i =>
        {
            Console.WriteLine("Step One: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;
        }).AsParallel().Select(i =>
        {
            Console.WriteLine("Step Two: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;

        }).ToList();

您將看到步驟1和步驟2是“序列”中的don,但是每次迭代都是在不同的線程上完成的。

但是,您的AsSequential()將在執行它的主線程上運行。

因此,我希望步驟1和步驟2 在與調用線程不同的同一線程上運行,但是步驟3在啟動鏈的同一線程上運行。

如果要實現您描述的行為,只需將查詢更改為此:

array.Select(i =>
        {
            Console.WriteLine("Step One: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;
        }).ToList().AsParallel().Select(i =>
        {
            Console.WriteLine("Step Two: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;

        }).AsSequential().Select(i =>
        {
            Console.WriteLine("Step Three: {0}", Thread.CurrentThread.ManagedThreadId);
            return i;
        }).ToList();

第一個ToList()評估將在調用線程上運行所有內容,AsParallel()然后在不同的線程上運行每個迭代(取決於ThreadPool的可用性),最后,您的AsSequential將確保順序位在調用線程上運行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM