好的,情况就是这样:我的主要/ UI线程(称之为Thread1)用于从phsycial文档扫描程序中获取一批图像。获取批次后,会启动一个单独的“后台”线程(称为Thread2)来处理并保存该批次中的图像。
Thread2(“后台”线程)正在使用Parallel.For
循环,与正常For
循环相比,可将图像处理/保存时间缩短70%。但是,它似乎也最大化了我的所有处理器,因此在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
}
答案 0 :(得分:5)
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;
});
答案 1 :(得分:2)
有没有办法“限制”一个Parallel.For循环,以便它不会最大化我的处理器?
是的,您可以添加MaxDegreeOfParallelism = N的选项。
或者设置处理优先级?
没有。它是一个ThreadPool(借用)线程。不要改变它的属性。实际上它是一堆池线程。
或者我误解了Parallel.For循环是如何工作的?是以某种方式阻止了Thread1吗?
是的,来自外部的Parallel.For(...)
是阻止来电。因此,在单独的Task或Backgroundworker上运行它,而不是从主线程运行它。
答案 2 :(得分: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
}
答案 3 :(得分:0)
如果不能访问整个应用程序,很难确切知道这里发生了什么,但让我们先打破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
}
因此,改变Thread2的优先级不会产生任何影响,因为它无论如何都会被阻止。但是,如果未阻止Thread1,它仍应能够运行。 Thread1可能无法经常运行,这可能是您的问题。
天真的方法是做一些像乱七八糟的线程优先级,计数或添加一些Thread.Yield()语句。但是,池中的线程可能已经阻塞,因为它们正在执行I / O.
最有可能的是,你需要做的是重构你的代码,以便你的图像加载循环阻止主线程的图像获取使用类似System.Threading.WaitHandle的东西或将主线程正在做的更多工作移动到图像加载。如果没有重构,经验表明,在最终的运行条件下,您将最终获得针对您正在测试的特定机器量身定制的解决方案,但是当负载发生变化或硬件发生变化时,您的“调整”将会关闭。
重新编写代码,以便在Parallel.For工作中完成更多工作,并在主线程工作时阻塞主线程活动上的线程,并且您将拥有一个令您自豪的解决方案。
答案 4 :(得分:0)
好的,我明白了!我只是张贴这个以防万一有人无意中发生了这种情况......
事实证明Parallel.For
线程没有阻止Thread1(是的,你没事)。然而,Thread1中的一个对象试图从循环移动的Thread
中抓取一个新的ThreadPool
,因此发生了“延迟”。我正在使用第三方SDK,它允许我与TWAIN接口进行交互,并且每次用户开始新扫描时都有一个选项ScanInNewThread = true
试图获取新线程(这是在循环时发生的)正在嘎吱嘎吱地走了。我能够改变这一点,以便在整个应用程序会话中使用单个(但仍然是单独的)线程,而不是为每个扫描批次抓取一个新线程,并且BANG,没有更明显的延迟。
所以 - 这个故事的寓意:
现有线程仍然应该“正常”运行(调用Parallel.For
循环的线程除外),只要它们在循环开始时不试图从ThreadPool
获取更多线程