Значение аргумента отличается внутри вызываемой функции - PullRequest
0 голосов
/ 25 апреля 2020

Я использую код ниже для создания нескольких задач в C#:

private static List<Task> _taskList = new List<Task>();
private static ConcurrentQueue<string> cred = new ConcurrentQueue<string>();

private static void TaskMethod1(string usercred)
{
    // I am doing a bunch of operations here, all of them can be replaced with
    // a sleep for 25 minutes.
    // After all operations are done, enqueue again.
    cred.Enqueue("usercred")
}

private static void TaskMethod()
{
    while(runningService)
    {
        string usercred;
        // This will create more than one task in parallel to run,
        // and each task can take up to 30 minutes to finish.
        while(cred.TryDequeue(out usercred))
        {
            _taskList.Add(Task.Run(() => TaskMethod1(usercred)));
        }
    }
}

internal static void Start()
{
    runningService = true;
    cred.enqueue("user1");
    cred.enqueue("user2");
    cred.enqueue("user3");
    Task1 = Task.Run(() => TaskMethod());
}

Я столкнулся со странным поведением в коде выше. Помещая точку останова в строку _taskList.Add(Task.Run(() => TaskMethod1(usercred)));, я проверяю значение usercred каждый раз, когда вызывается TaskMethod1, и при вызове оно не равно нулю, но в одном из случаев значение usercred внутри null TaskMethod1. Я понятия не имею, как это могло происходить.

Ответы [ 3 ]

1 голос
/ 26 апреля 2020

Вы должны объявить переменную usercred внутри внутреннего while l oop, чтобы лямбда внутри Task.Run захватила отдельную переменную для каждого l oop, а не одна и та же переменная для всех циклов.

while(runningService)
{
    while(cred.TryDequeue(out var usercred))
    {
        _taskList.Add(Task.Run(() => TaskMethod1(usercred)));
    }
}

В качестве примечания, я бы рассмотрел использование BlockingCollection вместо ConcurrentQueue, чтобы иметь возможность блокировать текущий нить до тех пор, пока предмет не станет доступным, так что мне не придется беспокоиться о непреднамеренном создании плотного l oop.

1 голос
/ 25 апреля 2020

Вы используете Task.Run, где вы используете переменные, в то время как l oop. Вы не передаете это в задачу. Таким образом, к моменту выполнения задачи ее значение меняется.

Вы должны использовать

while (runningService)
{
    string usercred;
    // This will create more than one task in parallel to run,
    // and each task can take upto 30 minutes to finish.
    while (cred.TryDequeue(out usercred))
    {
        _taskList.Add(Task.Factory.StartNew((data) => TaskMethod1(data.ToString()), usercred)
    }
}
0 голосов
/ 25 апреля 2020

Следующее изменение решило проблему.

private static void TaskMethod()
{
    while(runningService)
    {
        string usercred;
        // This will create more than one task in parallel to run,
        // and each task can take up to 30 minutes to finish.
        while(cred.TryDequeue(out usercred))
        {
            var uc = usercred;
            _taskList.Add(Task.Run(() => TaskMethod1(uc)));
        }
    }
}
...