Передача параметра в 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);
}
}
}