8

好的,情况如下:我的主/UI 线程(称为 Thread1)用于从物理文档扫描仪获取一批图像。获取批次后,将启动一个单独的“背景”线程(称为 Thread2)以处理和保存该批次中的图像。

Thread2(“后台”线程)正在使用一个Parallel.For循环,与正常循环相比,该循环将图像处理/保存时间减少了 70% For。但是,它似乎也使我的所有处理器都达到极限,因此 Thread1 在Parallel.For循环完成之前无法开始获取更多图像。

有没有办法“限制”一个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

一种粗略的方法是 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 回答
2

有没有办法“限制”一个 Parallel.For 循环,这样它就不会最大化我的处理器?

是的,您可以使用 MaxDegreeOfParallelism=N 添加选项。

或者设置处理优先级?

不,它是一个 ThreadPool(借用)线程。不要改变它的属性。实际上它是一堆池线程。

还是我误解了 Parallel.For 循环的工作原理?它是否以某种方式阻塞了 Thread1?

是的,从外面看Parallel.For(...)是阻塞电话。因此,在单独的任务或 Backgroundworker 上运行它,而不是从主线程。

于 2012-10-26T14:41:20.700 回答
1

好的,我想通了!我只是发布这个以防有人无意中发生这种情况......

事实证明,Parallel.For线程没有阻塞 Thread1(是的,你没问题)。但是,Thread1 中的一个对象试图ThreadThreadPoolwhile 循环中获取一个新对象,因此发生了“延迟”。我正在使用第 3 方 SDK,它允许我与 TWAIN 界面交互,并且有一个选项ScanInNewThread = true试图在每次用户开始新扫描时抓取一个新线程(这是在循环运行时发生的)。我能够更改这一点,以便在整个应用程序会话中使用单个(但仍然是单独的)线程,而不是为每个扫描批次抓取一个新线程,并且 BANG,没有更明显的延迟。

所以 - 故事的寓意:

现有线程应该仍然“正常”运行(调用Parallel.For循环的线程除外),只要它们不试图从ThreadPool循环进行中获取更多线程。

于 2012-11-01T21:15:57.487 回答
0

Without access to the entire application it is difficult to know exactly what is happening here but let's start by breaking down Parallel.For:

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
}

So changing Thread2's priority will not make a difference since it is blocked anyway. However, if Thread1 is not blocked, it should still be able to run. Thread1 may not run often, which could be your problem.

The naive approach would be to do something like mess with thread priorities, counts, or add some Thread.Yield() statements. However, the threads from the pool are likely already blocking since they are doing I/O.

Most likely, what you need to do here is refactor your code so that your image loading loop blocks on your main thread's image acquiring using something like System.Threading.WaitHandle or move more of the work the main thread is doing into the image loading. Without refactoring, experience says you will end up with a solution that is tailored to the specific machine you are testing on, under specific running conditions but, when loads change or hardware changes, your "tuning" will be off.

Rework the code so more work is being done inside your Parallel.For workers and block your threads on main thread activity when there is work for the main thread and you will have a solution of which you are proud.

于 2012-10-26T15:14:28.727 回答