C# — требования ThreadPool.QueueUserWorkItem()

У меня есть служба Windows, которая выполняет много работы одновременно. Я изучил потоки и нашел класс ThreadPool. В настоящее время я застрял, похоже, это не имеет никакого эффекта, это похоже на то, что я ставлю в очередь, никогда не запускается и не вызывается. В событии службы OnStart() я создаю такой поток:

Thread mainThread = new Thread(ReceiveMessages);
mainThread.Start();

Внутри метода ReceiveMessages() у меня есть процедура, которая проверяет очередь сообщений, а затем перебирает сообщения. Для каждой итерации я вызываю следующий код, чтобы выполнить некоторую работу с каждым сообщением:

ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state)
{
    Interpreter.InsertMessage(encoding.GetBytes(MessageBody));
}), null);

Я думаю, что синтаксис правильный, он компилируется без проблем, но я не могу не чувствовать, что я что-то упускаю. Когда я запускаю службу, ничего не происходит. Однако, если я заменю приведенный выше фрагмент кода следующим:

insertThread = new Thread(delegate() { Interpreter.InsertMessage(encoding.GetBytes(MessageBody)); });
insertThread .Start();

Это работает на 100%. Однако это не очень эффективно и может привести к сбою службы (что иногда происходит, поэтому я пытаюсь использовать вместо этого TheadPool). Кто-нибудь может пролить свет на эту тему?


person Mr. Smith    schedule 22.04.2010    source источник


Ответы (2)


Похоже, вы создаете замыкание над MessageBody в вашем обратном вызове ожидания. Если свойство MessageBody вызывающего объекта имеет значение null к тому времени, когда пул потоков выполняет рабочий элемент, то это то, над чем будет работать InsertMessage.

Вам нужно определить перегрузку Interpreter.InsertMessage, которая принимает объект и использует это в качестве вашего WaitCallback:

public void InsertMessage(object messageBody) {
    this.InsertMessage((byte[])messageBody);
}

Затем передайте байты тела сообщения в качестве второго параметра:

ThreadPool.QueueUserWorkItem(new WaitCallback(Interpreter.InsertMessage), 
                             encoding.GetBytes(MessageBody));
person Jeff Sternal    schedule 22.04.2010
comment
Это идеально! Работает как шарм! - person Mr. Smith; 23.04.2010

По умолчанию, когда вы создаете новый поток, этот поток является Поток переднего плана. Однако потоки ThreadPool имеют набор IsBackground. к истине.

Это означает, что поток пула потоков не будет поддерживать работоспособность вашего приложения. Вероятно, поэтому он никогда не "запускается" - он просто сразу выключается.

Однако это не очень эффективно и может привести к сбою службы (что иногда происходит, поэтому я пытаюсь использовать вместо этого TheadPool). Кто-нибудь может пролить свет на эту тему?

Самосозданный поток должен быть таким же эффективным (как только поток запущен и работает). Поток ThreadPool никоим образом не поможет "сбою" - вам все равно нужно будет соответствующим образом отладить свою службу.

person Reed Copsey    schedule 22.04.2010
comment
Я понимаю, что вы говорите. ReceiveMessages() имеет цикл while(true), который постоянно проверяет очередь сообщений, поэтому каждый раз, когда есть сообщения, каждое сообщение получает свой собственный поток для выполнения некоторой работы. Может, это как-то влияет? - person Mr. Smith; 23.04.2010
comment
@Г-н. Смит: Если основной поток завершится, вы закроете приложение (если вы используете потоки ThreadPool), так как вам нужно иметь хотя бы один не фоновый поток, чтобы поддерживать его в рабочем состоянии... - person Reed Copsey; 23.04.2010
comment
Да, но разве это не то, что я делаю в событии OnStart()? Вы сказали, что создание нового потока по умолчанию является потоком переднего плана, разве это не сделало бы mainThread в моем первом фрагменте кода над потоком, который поддерживал бы его? Я использую ThreadPool только внутри этого потока. Что вы думаете? - person Mr. Smith; 23.04.2010