Общий ThreadPool в .NET - PullRequest
       17

Общий ThreadPool в .NET

5 голосов
/ 27 ноября 2008

Вот довольно распространенная задача для меня, и, как мне кажется, для многих программистов .NET:
Я хочу использовать .NET ThreadPool для планирования рабочих потоков, которые должны обрабатывать задачи определенного типа.

В качестве переподготовки подписи для метода очередей ThreadPool и связанного с ним делегата:

public static bool QueueUserWorkItem (
    WaitCallback callBack,
    Object state
)
public delegate void WaitCallback (Object state)

Следовательно, типичный класс рабочих потоков общего вида будет выглядеть примерно так:

public class Worker<T> {
    public void schedule(T i_task) {
        ThreadPool.QueueUserWorkItem(execute, i_task)
    }
    private void execute(Object o){
        T task = (T)o;  //What happened to the type safety?  
        executeTask(task);
    }
    private void executeTask(T i_task){
        //process i_task
    }
}

Обратите внимание на тип параметра state? Это Object!

Какая убедительная причина, по которой команда .NET решила не делать метод QueueUserWorkItem (или весь класс ThreadPool) универсальным? Я не могу поверить, что они просто упустили это из виду.

Вот как бы я хотел это увидеть:

//in the ThreadPool class:
public static bool QueueUserWorkItem<T> (
    WaitCallback<T> callBack,
    T state
)
public delegate void WaitCallback<T> (T state)

Это сделало бы рабочий класс безопасным для типов (и намного понятнее, ИМХО):

public class Worker<T> {
    public void schedule(T i_task) {
        ThreadPool.QueueUserWorkItem<T>(execute, i_task)
    }
    private void execute(T i_task){
        //process i_task
    }
}

Я должен что-то упустить.

Ответы [ 3 ]

7 голосов
/ 27 ноября 2008

Поскольку упаковывать любое состояние по своему усмотрению тривиально, передавая анонимный делегат или лямбду в пул потоков (через захват переменных), нет необходимости в универсальной версии.

Например, вы можете написать служебную функцию:

static void QueueItem<T>(Action<T> action, T state)
{
    ThreadPool.QueueUserWorkItem(delegate { action(state); });
}

Но это было бы ужасно полезно, поскольку вы можете просто использовать делегата самостоятельно в любое время, когда вам нужно состояние в объединенной задаче.

5 голосов
/ 27 ноября 2008

Похоже, вы говорите о очереди на работу? (и я звучу как клиппист ...)

Для записи, потоки пула потоков обычно должны использоваться для коротких частей работы. В идеале вы должны создать свои собственные потоки для долгоживущей очереди. Обратите внимание, что .NET 4.0 может принимать библиотеки CCR / TPL, поэтому мы бесплатно получим несколько встроенных рабочих очередей, но нетрудно написать потоковую рабочую очередь. И вы можете сделать его также общим; -p

По вопросу - я предпочитаю подход с захваченными переменными для передачи состояния в потоки (будь то Thread, ThreadPool или Control.Invoke):

    Thread t = new Thread(() => SomeMethod(arg));
    t.IsBackground = true;
    t.Name = "Worker n";
    t.Start();

Это дает вам гораздо более детальный контроль над нитью, без насыщения ThreadPool.

1 голос
/ 27 ноября 2008

ThreadPool существует начиная с .NET 1.1, у которого не было универсальных элементов.

Мне нравится, как они решили не нарушать обратную совместимость: -)

...