8

わかりました。状況は次のとおりです。メイン/UIスレッド(Thread1と呼びます)は、物理ドキュメントスキャナーから画像のバッチを取得するために使用されます。バッチが取得されると、別の「バックグラウンド」スレッド(Thread2と呼ばれます)が起動して、そのバッチからの画像を処理および保存します。

Thread2(「バックグラウンド」スレッド)はParallel.For、通常のループよりも画像処理/保存時間を70%短縮するループを使用していForます。Parallel.Forただし、ループが完了するまでThread1がそれ以上画像の取得を開始できないように、すべてのプロセッサが最大になっているようにも見えます。

ループを「制限」Parallel.Forして、プロセッサが最大にならないようにする方法はありますか?または、処理の優先順位を設定するには?設定Thread2.Priority = ThreadPriority.Lowestしてみましたが、ループに影響がないようです。Parallel.Forそれとも、ループがどのように機能するかを誤解していますか?どういうわけかThread1をブロックしていますか?

これが、Thread1のメソッドからThread2を呼び出す方法です。

public void SaveWithSettings(bool save) // method in Thread1
{
    ....
    Thread thr = new Thread(ThreadWork); // creating new thread (Thread 2)
    thr.Priority = ThreadPriority.Lowest; // does nothing?
    thr.Start(new SaveContainer(sc)); // pass a copy as paramater

    // misc stuff to make scanning possible again
    numBgw++;
    twain.RemoveAllImages(); // clear images
    imagelist.Clear(); // clear imagelist images
    .... // etc. this all appears to process fine while Thread2 is processing
}

これが私のThreadWork方法です:

private void ThreadWork(object data) // executing in Thread2
{
    SaveContainer sc = data as SaveContainer; // holds images

    bool[] blankIndex = new bool[sc.imagelist.Count]; // to use in Parallel.For loop
    for (int i = 0; i < sc.imagelist.Count; i++)
        blankIndex[i] = false; // set default value to false (not blank)

    Parallel.For(0, sc.imagelist.Count, i => // loop to mark blank images
    {
        bool x = false; // local vars make loop more efficient
        x = sc.IsBlankImage((short)i); // check if image at index i is blank
        blankIndex[i] = x; // set if image is blank
    }
    .... // other image processing steps
}
4

5 に答える 5

7
public static void PriorityParallelForeach<T>(this IEnumerable<T> source, Action<T> action, ThreadPriority threadPriority, int? maxDegreeOfParallelism = null)
   {
       if (maxDegreeOfParallelism == null || maxDegreeOfParallelism<1)
       {
           maxDegreeOfParallelism = Environment.ProcessorCount;
       }

       var blockingQueue = new BlockingCollection<T>(new ConcurrentQueue<T>(source));
       blockingQueue.CompleteAdding();

        var tasks = new List<Task>() ;

        for (int i = 0; i < maxDegreeOfParallelism; i++)
        {
            tasks.Add(Task.Factory.StartNew(() =>
             {
                 while (!blockingQueue.IsCompleted)
                 {
                     T item;
                     try
                     {
                         item = blockingQueue.Take();
                     }
                     catch (InvalidOperationException)
                     {
                         // collection was already empty
                         break;
                     }

                     action(item);
                 }
             }, CancellationToken.None,
                  TaskCreationOptions.None,
                  new PriorityScheduler(threadPriority)));
        }

        Task.WaitAll(tasks.ToArray());

   }

あるいは単に:

Parallel.ForEach(testList, item =>
            {

                var priviousePrio = Thread.CurrentThread.Priority;
                // Set your desired priority
                Thread.CurrentThread.Priority = ThreadPriority.Lowest;

                TestCalc(item);

                //Reset priviouse priority of the TPL Thread
                Thread.CurrentThread.Priority = priviousePrio;
            });
于 2014-02-21T12:57:13.943 に答える
2

Parallel.Forループを「制限」して、プロセッサが最大にならないようにする方法はありますか?

はい、MaxDegreeOfParallelism=Nでオプションを追加できます。

または、処理の優先順位を設定するには?

いいえ。これはThreadPool(借用)スレッドです。そのプロパティを変更しないでください。実際には、それはプールスレッドの束です。

または、Parallel.Forループがどのように機能するかを誤解していますか?どういうわけかThread1をブロックしていますか?

はい、外部からのParallel.For(...)呼び出しはブロックされています。したがって、メインスレッドからではなく、別のTaskまたはBackgroundworkerで実行します。

于 2012-10-26T14:41:20.700 に答える
2

大まかな方法​​は、ParallelOptionsのMaxDegreeOfParallelismフラグです。

var Options = new ParallelOptions();

// Keep one core/CPU free...
Options.MaxDegreeOfParallelism = Environment.ProcessorCount - 1;

Paralle.For(0, sc.imagelist.Count, Options, i => // loop to mark blank images
{
    bool x = false; // local vars make loop more efficient
    x = sc.IsBlankImage((short)i); // check if image at index i is blank
    blankIndex[i] = x; // set if image is blank
}
于 2012-10-26T15:07:25.517 に答える
1

OK、わかりました!誰かがうっかりしてこれが起こった場合に備えて、これを投稿するだけです...

Parallel.ForスレッドがThread1をブロックしていなかったことがわかりました(はい、大丈夫でした)。ただし、Thread1のオブジェクトは、ループがクランチThreadしているThreadPool間に新しいものを取得しようとしていたため、「遅延」が発生しました。TWAINインターフェイスと対話できるサードパーティのSDKを使用してScanInNewThread = trueおり、ユーザーが新しいスキャンを開始するたびに新しいスレッドを取得しようとするオプションがありました(ループがクランチしている間に発生していました)。これを変更して、スキャンバッチごとに新しいスレッドを取得する代わりに、アプリケーションセッション全体で単一の(ただしまだ別個の)スレッドが使用されるようにしました。BANGは、目立った遅延がなくなりました。

SO-物語の教訓:

Parallel.For既存のスレッドは、ループの進行中にさらにスレッドを取得しようとしない限り、(ループを呼び出すスレッドを除いて)「正常に」機能するはずThreadPoolです。

于 2012-11-01T21:15:57.487 に答える
0

アプリケーション全体にアクセスできないと、ここで何が起こっているのかを正確に知ることは困難ですが、Parallelを分解することから始めましょう。

private void ThreadWork(object data) // executing in Thread2
{
    // Thread2 running here
    SaveContainer sc = data as SaveContainer; // holds images

    bool[] blankIndex = new bool[sc.imagelist.Count]; // to use in Parallel.For loop
    for (int i = 0; i < sc.imagelist.Count; i++)
        blankIndex[i] = false; // set default value to false (not blank)

    // Thread2 blocks on this call
    Paralle.For(0, sc.imagelist.Count, i => // loop to mark blank images
    {
        // Thread from the pool is running here (NOT Thread2)!!!

        bool x = false; // local vars make loop more efficient
        x = sc.IsBlankImage((short)i); // check if image at index i is blank
        blankIndex[i] = x; // set if image is blank
    }
    // Thread2 resumes running here

    .... // other image processing steps
}

したがって、Thread2の優先度を変更しても、とにかくブロックされるため、違いはありません。ただし、Thread1がブロックされていない場合でも、実行できるはずです。Thread1は頻繁に実行されない可能性があり、これが問題になる可能性があります。

単純なアプローチは、スレッドの優先順位やカウントを混乱させるようなことを行うか、Thread.Yield()ステートメントを追加することです。ただし、プールからのスレッドはI / Oを実行しているため、すでにブロックされている可能性があります。

ほとんどの場合、ここで行う必要があるのは、System.Threading.WaitHandleなどを使用してメインスレッドの画像取得で画像読み込みループがブロックされるようにコードをリファクタリングするか、メインスレッドが実行している作業の多くを画像読み込みに移動することです。リファクタリングを行わないと、特定の実行条件下でテストしている特定のマシンに合わせたソリューションが得られますが、負荷が変化したりハードウェアが変化したりすると、「チューニング」がオフになります。

コードを作り直して、Parallel内でより多くの作業が行われるようにします。ワーカーの場合、メインスレッドの作業があるときにメインスレッドのアクティビティでスレッドをブロックすると、誇りに思うソリューションが得られます。

于 2012-10-26T15:14:28.727 に答える