开发者

C# Passed state object to Task gets changed

I'm fighting with this for days, I hope You can push me in the right direction.

This is a recursive threading algorithm which parses a resource in a thread looking for links to other resources storing them in a ConcurrentBag for future TakeOuts. Threads creation is limited by an array with configurable size to preserve resources.

I have a private static ConcurrentBag<string> which gets filled by many threads. These are Tasks that are stored in private static Task[] with configurable size (app preferences).

There is a loop in Main that's doing TryTake() into local string url variable. When successfull it loops the Task[] trying to find empty slot creating new Task passing state object url and storing it in Task[] like this:

TaskArray[x] = new Task(FindLinks, url, TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);

The FindLinks is declared as

private static readonly Action<object> FindLinks = input => { ... }

In the main Task[] loop I am setting url to null before next TryTake(out url).

What is my problem here is the state object input that is passed from url in the main loop becomes null inside the Task lambda function. I've read almost all MSDN articles about TPL and can't figure this one out :(

How can I pass a variable (string) to the Task safely without closure (or whatever it is happening).

Any other ideas about improving this algorithm are welcome too.

Thanks.

Edit:

I have solved the problem by reordering statements and slightly rewriting the code in the main loop. No more assigning null to the variable. I suspect it was caused by compiler's statement reordering or preemption. Here is what it looks like now causing no more troubles:

string url;
if (CollectedLinks.TryTake(out url))
{
  var queued = false;
  while (!开发者_JAVA技巧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;
        }
      }
    }
  }
}


I have solved the problem by reordering statements and slightly rewriting the code in the main loop. No more assigning null to the variable. I suspect it was caused by compiler's statement reordering or preemption. Here is what it looks like now causing no more troubles:

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;
        }
      }
    }
  }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜