我有一个应用程序从一个包含100s-1000s行的文本文件中读取。
使用parallelOptions使用parallel.foreach处理每一行,以限制触发的任务量。这是“控制器”任务。
在这个“控制器”中,parallel.foreach是另一个执行实际工作的parallel.foreach。触发的每个控制器任务将执行相同的工作,使用原始文件行中指定的不同数据输入。同样,这项工作parallel.foreach正在使用并行选项来限制已启动的任务量。
在我上次的测试中,我用过 控制器foreach:MaxDegreeOfParallelism = 4 工人foreach:MaxDegreeOfParallelism:4
根据我的数学,应该意味着任何时候最多有16个任务在工作。
但是,当我检查perfmon.exe时,我可以看到我的应用程序使用700个线程。再过几个小时,这将超过1000个。
这怎么可能?为什么GC不收集这些完成的线程?
以前,我的代码在Thread []中触发了实际线程并遇到同样的问题。然后我把它移到了Task []并遇到了同样的问题。我假设某处存在线程泄漏,并且引用仍然指向线程/任务。我花了很多时间寻找这个无济于事。
所以我转向了parallel.foreach,并认为如果我从未创建任务的引用,那么任务泄漏就不会发生,因为这一切都发生在lamda中。
但问题仍然存在。有关为什么会这样的想法?或者这是正常的吗?
添加了下面的代码,由于所有测试并尝试调试此问题,它有点乱,尝试将其清理一下。
public static void RunActions(
List<paramsActionSettings> listActions,
string[] arrList,
int numThreads,
string domain = null,
delGetParamsActionSettings delGetActionsList = null,
delProcessString callbackActionsComplete = null
)
{
int iCntr= 0;
int iTotal = arrList.Length;
ParallelOptions prlOptions = new ParallelOptions
{
MaxDegreeOfParallelism = numThreads
};
//foreach (string listItemIter in arrList)
object oLock = new object();
Parallel.ForEach(arrList, prlOptions,(listItemIter) =>
{
lock (oLock)
{
Console.WriteLine("starting "+iCntr + " of " + iTotal + " run actions");
iCntr++;
}
string listItemCopySafe = string.Copy(listItemIter);
bool bCanDo = true;
List<paramsActionSettings> listActionsUse;
if (listActions == null)
{
listActionsUse = delGetActionsList();
}
else
{
listActionsUse = listActions;
}
foreach (paramsActionSettings prms in listActionsUse)
{
if (prms.delCanDo != null && !prms.delCanDo(listItemCopySafe, domain))
{
bCanDo = false;
break;
}
}
if (!bCanDo) return;
List<paramsFire> listParams = new List<paramsFire>();
//create a list of paramsfire objects, the object holds the params and the delfunction
foreach (paramsActionSettings prms in listActionsUse)
{
listParams.Add(new paramsFire(prms.delGetDoParams(listItemCopySafe), prms.delDoSomething));
}
FireActions(listParams, callbackActionsComplete, listItemCopySafe);
Console.WriteLine("Finished " + iCntr + " of " + iTotal );
});
}
private static void FireActions(List<paramsFire> list, delProcessString callbackActionsComplete, string itemArr)
{
int icntr = 0;
foreach (paramsFire prms in list)
{
try
{
if (icntr == 0)
{
if (!prms.delDoSomething(prms.oParams))
{
break;
}
}
else
{
prms.delDoSomething(prms.oParams);
}
icntr++;
}
catch (Exception e)
{
ErrorLog.WriteLine("foreach (paramsFire prms in list)");
UtilException.Dump(e, "foreach (paramsFire prms in list)");
}
}
if (callbackActionsComplete != null)
{
try
{
callbackActionsComplete(itemArr);
}
catch { }
}
}
答案 0 :(得分:1)
显然,问题不在于任何特定的API,因为您说线程,任务和Parallel.ForEach
的问题是相同的。
不要问为什么框架没有完成它的工作(因为它是)。问一下,为什么你的代码会产生比你想象的更多的线程。没有看到更多代码,这个问题就无法得到完全解答。
答案 1 :(得分:1)
我做了一点改写。它在我的机器上只有22个线程(8个proc盒子)。随意使用任何这些变化:
public static void RunActions(
IEnumerable<paramsActionSettings> listActions,
IEnumerable<string> arrList,
int numThreads,
string domain = null,
delGetParamsActionSettings delGetActionsList = null,
delProcessString callbackActionsComplete = null)
{
var cntr = 0;
var total = arrList.Count();
var prlOptions = new ParallelOptions
{
MaxDegreeOfParallelism = numThreads
};
////foreach (var listItemIter in arrList)
Parallel.ForEach(arrList, prlOptions, listItemIter =>
{
Interlocked.Increment(ref cntr);
Console.WriteLine("starting " + cntr + " of " + total + " run actions");
var listItemCopySafe = string.Copy(listItemIter);
var listActionsUse = listActions ??
((delGetActionsList == null) ? new paramsActionSettings[0] : delGetActionsList());
var canDo = listActionsUse.All(prms => prms.delCanDo == null
|| prms.delCanDo(listItemCopySafe, domain));
if (!canDo)
{
return;
}
var listParams = listActionsUse.Select(prms => new paramsFire(
prms.delGetDoParams(listItemCopySafe),
prms.delDoSomething));
// create a list of paramsfire objects, the object holds the params and the delfunction
FireActions(listParams, callbackActionsComplete, listItemCopySafe);
Console.WriteLine("Finished " + cntr + " of " + total);
});
}
private static void FireActions(
IEnumerable<paramsFire> list,
delProcessString callbackActionsComplete,
string itemArr)
{
var icntr = 0;
foreach (var prms in list)
{
try
{
if (icntr == 0)
{
if (!prms.delDoSomething(prms.oParams))
{
break;
}
}
else
{
prms.delDoSomething(prms.oParams);
}
icntr++;
}
catch (Exception e)
{
ErrorLog.WriteLine("foreach (paramsFire prms in list)");
UtilException.Dump(e, "foreach (paramsFire prms in list)");
}
}
if (callbackActionsComplete == null)
{
return;
}
try
{
callbackActionsComplete(itemArr);
}
catch
{
}
}