1

素数を印刷するための簡単なコンソールアプリケーションを作成しました。数が素数かどうかをチェックする関数にThreadPoolを使用しています。

タスクマネージャーで、このプログラムが大量のメモリを消費し始めます(数秒で1 GB)。それでもThreadPoolを使用する必要がある場合、どうすればそれを改善できますか?

これが私が書いたコードです

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(2);
        Console.WriteLine(3);
        Console.WriteLine(5);
        Console.WriteLine(7);
        Console.WriteLine(11);
        Console.WriteLine(13);
        Console.WriteLine(17);
        for (long i = 19; i < Int64.MaxValue; i = i+2)
        {
            if(i % 3 == 0 || i % 5 == 0 || i % 7 == 0 || i % 11 == 0 || i % 13 == 0 || i % 17 == 0 )
                continue;

            ThreadPool.QueueUserWorkItem(CheckForPrime, i);
        }
        Console.Read();
    }

    private static void CheckForPrime(object i)
    {
        var i1 = i as long?;
        var val =  Math.Sqrt(i1.Value);
        for (long j = 19; j <= val; j = j + 2)
        {
            if (i1 % j == 0) return;
        }
        Console.WriteLine(i1);

    }
}
4

3 に答える 3

4

コードを修正する最も簡単な方法は、セマフォを使用してワーク キューを制限することです。

class Program
{
    // Max 100 items in queue
    private static readonly Semaphore WorkLimiter = new Semaphore(100, 100);

    static void Main(string[] args)
    {
        Console.WriteLine(2);
        Console.WriteLine(3);
        Console.WriteLine(5);
        Console.WriteLine(7);
        Console.WriteLine(11);
        Console.WriteLine(13);
        Console.WriteLine(17);

        for (long i = 19; i < Int64.MaxValue; i = i + 2)
        {
            if (i % 3 == 0 || i % 5 == 0 || i % 7 == 0 || i % 11 == 0 || i % 13 == 0 || i % 17 == 0)
                continue;

            // Get one of the 100 "allowances" to add to the queue.
            WorkLimiter.WaitOne();
            ThreadPool.QueueUserWorkItem(CheckForPrime, i);
        }
        Console.Read();
    }

    private static void CheckForPrime(object i)
    {
        var i1 = i as long?;
        try
        {
            var val = Math.Sqrt(i1.Value);
            for (long j = 19; j <= val; j = j + 2)
            {
                if (i1%j == 0) return;
            }
            Console.WriteLine(i1);
        }
        finally
        {
            // Allow another add to the queue
            WorkLimiter.Release();
        }
    }
}

これにより、キューをいっぱいにしたり、Sleep.

于 2013-02-08T07:53:47.400 に答える
3

率直に言えば、マルチスレッド化が間違っています。スレッドは、正しく使用すれば強力なツールですが、すべてのツールと同様に、すべての場合に適切なソリューションとは限りません。ガラス瓶はビールを入れるのには適していますが、釘を打つのにはあまり適していません。

一般的に、スレッドを増やしても実行速度が向上するわけではありません。これは、ご存じのように、ここでは特に当てはまります。作成したコードは、ループを反復するたびに新しいスレッドをキューに入れ、それらのスレッドのそれぞれがスタックを割り当てます。.NET の世界でのスタックのデフォルト サイズは 1 MB であるため、メモリ コミットメントが急上昇するのにそれほど時間はかかりません。したがって、1 GB を超えていても特に驚くことではありません。最終的には、厳しいメモリ制限に達し、OutOfMemoryExceptionスローされます。また、メモリは、設計によってシステムがすぐに不足する最も明白なリソースです。システム リソースがスレッド プールで指数関数的に増加しない限り、パフォーマンス上の利点は得られません。

Adil はThread.Sleep、ループを続行する (そして追加のスレッドを作成する) 前に、作成した新しいスレッドを実行する時間を与えるために への呼び出しを挿入することを提案しています。コメントで述べたように、これは「機能します」が、私にはひどく醜いハックのように思えます。しかし、本当の問題は設計にあるため、より良い解決策を提案することは困難です。あなたはスレッドプールを使用しなければならないと言っていますが、なぜそうなのかは言いません。

どうしてもスレッド プールを使用しなければならない場合、最善の回避策はおそらく、スレッド プールのサイズに任意の制限を設定することです (つまり、スレッド プールがSetMaxThreads生成できる新しいスレッドの数) 。これは、少なくともThread.Sleep.

注: このSetMaxThreads方法を採用する場合は、最大値を最小値未満に設定できないことに注意してください。最小値のデフォルト値は CPU コアの数であるため、デュアルコア プロセッサを使用している場合、最初に最小値を下げずに最大値を 1 に設定することはできません。

最後に、この場合の答えは実際には変わりませんが、タスク マネージャーはメモリ プロファイラーではないことに注意してください。それをあたかもそれであるかのように信頼すると、悪い (または少なくとも非常に誤解を招く) データが得られることがよくあります。

編集:さらに考えた結果、問題は実際には指数関数的な実行ではなく、指数関数的なクエリにあることがわかりました。許可されたスレッドの最大数はおそらく関係ありません。これは、コードが処理を期待できるよりも速くキューに入れるためです。したがって、サイズを制限することは気にしないでください。おそらく、セマフォの作成を含むJoachim のソリューション、またはスレッドプールを使用しないことについて誰もが行った暗黙の提案を使用したいと思うでしょう。

于 2013-02-08T07:41:04.787 に答える
1

threads途切れることなくループで作成しています。でさらにスレッドを作成する前に、スレッドの一部が実行を終了するように、スレッドの作成プロセスを中断する必要がありますThreadPoolそのためにSystem.Threading.Thread.Sleepを使用できます。

for (long i = 19; i < Int64.MaxValue; i = i+2)
{
      if(i % 3 == 0 || i % 5 == 0 || i % 7 == 0 || i % 11 == 0 || i % 13 == 0 || i % 17 == 0 )
            continue;
      ThreadPool.QueueUserWorkItem(CheckForPrime, i);
      System.Threading.Thread.Sleep(100);
}

スレッドを使用する場所を知っておく必要があります。スレッドは有益であり、必要なスレッドの数と、アプリケーションのパフォーマンスに対するスレッドの影響は何でしょうか。現在のスレッドを一時停止する時間は、アプリケーションによって異なります。私はちょうど100ミリ秒を与えました、あなたはあなたのアプリケーションに従って調整します。

于 2013-02-08T07:13:21.540 に答える