【发布时间】:2011-04-15 07:28:29
【问题描述】:
我为此奋斗了好几天,希望你能把我推向正确的方向。
这是一种递归线程算法,它解析线程中的资源,寻找指向其他资源的链接,将它们存储在ConcurrentBag 中以供将来取出。线程创建受限于可配置大小的数组以保留资源。
我有一个private static ConcurrentBag<string>,它被许多线程填充。这些Tasks 存储在private static Task[] 中,具有可配置的大小(应用首选项)。Main 中有一个循环将 TryTake() 转换为本地 string url 变量。当成功时,它循环 Task[] 试图找到空槽创建新的 Task 传递状态对象 url 并将其存储在 Task[] 中,如下所示:
TaskArray[x] = new Task(FindLinks, url, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
FindLinks 被声明为
private static readonly Action<object> FindLinks = input => { ... }
在主Task[] 循环中,我将url 设置为null,然后再下一个TryTake(out url)。
我的问题是从主循环中的url 传递的状态对象input 在Task lambda 函数中变成null。我已经阅读了几乎所有关于 TPL 的 MSDN 文章,但无法弄清楚这一点:(
如何在不关闭(或发生任何情况)的情况下安全地将变量(字符串)传递给Task。
也欢迎任何其他关于改进此算法的想法。
谢谢。
编辑:
我通过重新排序语句和稍微重写主循环中的代码解决了这个问题。不再将 null 分配给变量。我怀疑这是由编译器的语句重新排序或抢占引起的。这是它现在的样子,不会再造成麻烦了:
string url;
if (CollectedLinks.TryTake(out url))
{
var queued = false;
while (!queued)
{
// Loops thru the array looking for empty slot (null)
for (byte i = 0; i < TaskArray.Length; i++)
{
if (TaskArray[i] == null)
{
TaskArray[i] = new Task(FindLinks, url, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
TaskArray[i].Start(TaskScheduler.Current);
queued = true; break;
}
}
if (!queued)
{
// Loop and clean the array
for (var i = 0; i < TaskArray.Length; i++)
{
if (TaskArray[i] == null)
continue;
if (TaskArray[i].Status == TaskStatus.RanToCompletion || TaskArray[i].Status == TaskStatus.Canceled || TaskArray[i].Status == TaskStatus.Faulted)
{
TaskArray[i].Wait(0);
TaskArray[i] = null;
}
}
}
}
}
【问题讨论】:
-
您已经描述了很多代码,但如果您能真正显示这些代码,那就更清楚了。很难理解正在发生的事情。特别是,答案很容易取决于您是否捕获了任何变量。
-
我明白了。不幸的是,代码是如此复杂和重构,以至于我需要一些时间来收集所有必要的部分并过滤掉其余部分,并且您可能需要同样的时间才能在脑海中将它们链接在一起。不过,我会尽快做到最好。
-
如果您已经解决了问题,看起来您已经解决了,您应该将解决方案发布为答案并将其标记为已回答。这将让其他人更快地知道问题已经解决。
标签: c# multithreading task task-parallel-library