2017-07-31 13 views
1

リストビルダーのメソッドがたくさんの文字列(1百万項目)を含む文字列のIEnumerableを返し、それを文字列のリストに格納した後、すべての項目をParallel.ForeachのStringBuilderインスタンスです。その後、私はstringBuilderInstance.Lengthを印刷しました。ConcurrentBagはスレッドセーフではないようです

問題は1000000未満でした。 の後に、私はリストコレクションがスレッドセーフではないことに気づきました。これがこの問題の原因です。

1)を使用しロック

2)ConcurrentBag

私はロックを使用していた場合、それはOKで、長さは1 milionですが、を使用してください: だから2ソリューションは、私の心を交差します:

文字列のConcurrentBagを使用しているとき、長さが予想よりも短くなっています。

この問題の根本的な原因は何ですか?

リスト・クリエーター方法:

public static void DoWithParallel_ThreadSafe() 
{ 
    ConcurrentBag<string> listOfString = new ConcurrentBag<string>(CreateList()); 
    StringBuilder a = new StringBuilder(); 
    Action<string> appender = (number) => 
    { 
     a.Append(number); 
    }; 
    Parallel.ForEach(listOfString, appender); 
    Console.WriteLine($"The string builder lenght : {a.Length}"); 
} 

使用してロック:ConcurrentBagを使用して

public static List<string> CreateList() 
{ 
    List<string> result = new List<string>(); 
    for (int i = 0; i < 1000000; i++) 
    { 
     result.Add(1.ToString()); 
    } 
    return result; 
} 

public static void DoWithParallel_UnsafeThread_Lock() 
{ 
    List<string> listOfString = CreateList(); 
    StringBuilder a = new StringBuilder(); 
    Action<string> appender = (number) => 
    { 
     lock (listOfString) 
     { 
      a.Append(number); 
     } 
    }; 
    Parallel.ForEach(listOfString, appender); 
    Console.WriteLine($"The string builder lenght : {a.Length}"); 
} 

メイン:

static void Main(string[] args) 
{ 
    DoWithParallel_UnsafeThread_Lock(); 
    DoWithParallel_ThreadSafe(); 
    Console.ReadKey(); 
} 

ありがとうございます。

+3

スレッドセーフなコードを書く前に、スレッディングについて学ぶ必要があります。 – SLaks

+2

http://blog.slaks.net/2013-07-22/thread-safe-data-structures/ – SLaks

+3

Parallel.ForEachがスレッドセーフな方法で最初の引数に渡すIEnumerableを呼び出すことがわかりましたその引数のために渡していたコレクションは関係ありません。 –

答えて

4

StringBuilderではありません。スレッドセーフであるため、一部のAppend()コールは「失われています」。したがって、コレクションがスレッドセーフであっても、ロックが必要です。コードが動作しない理由をするときに

(また、あなたがすべてで、スレッドセーフであることが、コレクションを必要としない理由についてServyの回答を参照してください。)

+3

ここにロックするべきではありません。正しいソリューションは、複数のスレッドを作成するのではなく、それらのすべてを同期させるためだけのスレッドを作成することではありません。 – Servy

+0

しかし、stringbuilderの代わりにstringを使用すると、それは私に間違った答えも与えます。文字列はスレッドセーフです。私は正しいですか? – Parsa

+4

@Parsa:文字列は不変です。あなたの変数はスレッドセーフではありません。 – SLaks

5

StringBuilderは、したがって、複数のスレッドから突然変異させることができませんそれを試みてください。 lockigは意味がないことに注意してください。作業を複数のスレッドから行うことができないため、最初に作業を行う複数のスレッドを作成しないでください。複数のスレッドからConcurrentBagにアクセスすることは決してないので、Listの代わりにそれを使用するのは無意味です。

関連する問題