Передача параметра в Task.Factory.StartNew

С учетом следующего кода:

string injectedString = "Read string out of HttpContext";
Task.Factory.StartNew(() =>
 {
    MyClass myClass = new MyClass();
    myClass.Method(injectedString);
 }

Это лучший способ передать строку в Task/Thread?

Мои проблемы с этим методом:

  • Знает ли сборщик мусора, когда строка перестала контекст и правильно очистить его?
  • Есть ли лучший способ встраивать зависимости в задачу, нарушающую ссылку на объект в основном потоке?

Это в веб-сервисе Asp.Net, если это имеет значение и является потоком огня и забытого типа, я не жду никакого ответа.

Моя строка на самом деле будет считываться из HttpContext, что является одной из причин, по которой я вставляю ее таким образом (нить не имеет доступа к вызывающим потокам HtppContext)

Ответ 1

Ваша лямбда будет выведена в класс, сгенерированный компилятором. Переменная injectedString станет полем этого класса.

Таким образом, это будет сбор мусора, когда сгенерированный класс выходит за рамки (который в основном находится в самом конце вашей лямбда), и GC решает выполнить сборку.

В ответ на ваш комментарий:

Нет дублирования. Компилятор делает следующее:

 string injectedString = "Read string out of HttpContext";
 Task.Factory.StartNew(() =>
 {
    MyClass myClass = new MyClass();
    myClass.Method(injectedString);
 }

В это:

CompilerGeneratedClass c1 = new CompilerGeneratedClass();
c1.injectedString = "Read string out of HttpContext";
// call delegate here.

Вспомните также: Строки интернированы в среде CLR. Даже если код был дублирован, строковые литералы будут интернированы в пуле. У вас, по существу, будет только родной WORD размер дубликата, который указывает на строку (только литералы строки).

Ответ 2

Вероятно, вы должны использовать перегрузку Task.Factory.StartNew(Action<object> action, object state) для передачи состояния в новую задачу.

Task.Factory.StartNew((object myState) => {
    var i = (int)myState;

    //Do calculations...
    var x = i + 10; 
}, 10);

Ответ 3

Если вас беспокоит injectedString, может быть "сбор мусора" до запуска myClass.Method(injectedString);?

Ответ нет. Вам не нужно об этом беспокоиться, потому что injectedString больше не является локальной переменной, когда она закрыта в lambda. Он станет полем нового класса, сгенерированного компилятором.

Если вас беспокоит, сборщик мусора соберет его в нужное время? Ответ да. Он будет действовать, если экземпляр этого класса выходит из области действия и не ссылается на него в управляемом коде. Это произойдет, конечно, после завершения Task и выхода из области действия.

Ответ 4

 class Program
    {
        static readonly ConcurrentQueue<int> mockItems = new ConcurrentQueue<int>(Enumerable.Range(0, 100));
        static CancellationToken token;
        static CancellationTokenSource tokenSource;
        static void Main(string[] args)
        {
            tokenSource = new CancellationTokenSource();
            token = tokenSource.Token;

            for (int i = 0; i < 5; i++)
            {
                Task.Factory.StartNew(delegate { StartProcess(); }, tokenSource.Token, TaskCreationOptions.LongRunning);
            }
            Console.Read();
            tokenSource.Cancel();

        }
        private static void StartProcess()
        {
            while (true)
            {
                if (token.IsCancellationRequested)
                    break;

                int result;
                mockItems.TryDequeue(out result);

                Console.WriteLine(result);
                Thread.Sleep(1000);
            }

        }
    }