Re: пул потоков. общие ресурсы.
От: maxman20  
Дата: 15.02.11 08:45
Оценка:
Здравствуйте, fryme, Вы писали:


F>Я почти ничего не смыслю в многопоточном программировании, поэтому прошу помощи у людей разбирающихся.

F>Имеется вот такой код:

F>class ThreadPool;


F>struct ThreadData {

F> ThreadPool *pool;
F>}

F>unsigned __stdcall thread_run(void *args) {

F> ThreadData *data = static_cast<ThreadData *> (args);
F> Task *task = data->pool->getTask();
task->>invoke();
F>}


F>class ThreadPool {

F>public:
F> ThreadPool() {...}

F> void startAllThreads(int n) {

F> ThreadData *data = new ThreadData;
data->>pool = this;
F> while (m_lThreads.size() != n) {
F> hThread = (HANDLE)_beginthreadex( NULL, 0, &thread_run, (void *)data, 0, &threadID );
F> m_lThreads.push_back( hThread );
F> }
F> }

F> Task *getTask() {

F> Task *task = m_taskQueue->pop();
F> return task;
F> }

F>}


F>int main() {

F> ThreadPool pool;
F> pool.startAllThreads(3);
F> for(;) {}
F>}

F>Код реализует механизм пула потоков. Содаётся Объект ThreadPool, который раздаёт задания созданным тредам.


F>Как я понимаю общий принцип работы такой:

F>- создаётся процесс main()
F>- он создаёт нужное количество тредов
F>- main становится в бесконечный цикл

F>while (true) {

F> — каждый тред запрашивает у ThreadPool задание
F> — когда задание выполненно запрашивается следующее
F>}

F>Но при этом непонятно:

F>1) если main выполняет цикл for(;) {}, то как же он при этом отвечает на запросы тредов? я не могу понять как будет выглядеть общая картина взаимодействия потоков и процесса.
F>2) Разве объект m_taskQueue при этом будет корректно отрабатывать запросы? Или нужно блочить очередь заданий через мьютексы или другие примитивы синхронизации?
F>3) А как грамотно сделать блокировку потока в среде Win?
F>4) При блокировке треда что с ним происходит? Ему системно выставляется какой то статус?

F>Есть некоторая специфика всего механизма пула потоков из-за которой приходится писать это самому, а не использовать какие то готовые решения (правда толковых я не нашёл, поэтому если у кого то есть что то подходящее с удовольствием изучу).



F>Заранее спасибо всем откликнувшимся!



1. В цикле внутри main лучше всего получать задания, например вызывать функцию ожидающую пользовательского ввода
и по результату создавать объект Tasк, который нужно поместь в очередь заданий. Не занятые работой потоки пула
должны ожидать появления заданчи в очереди. При появлении задачи один из потоков просыпаться вытаскивает ее из
очереди и начинает обработку. После завершения обработки, поток должен опять попытаться получить задачу из очереди,
а если очередь пуста заблокироваться на условной переменной.

2,3. Нужно использовать условные переменные например из boost.thread или ищите в API.



например c помощью boost.thread можно так:

class Task
{
public:
void execute();// выполнить задачу
};

Task* askUserForTask(); // получить задачу


class ThreadPool
{
public:

void startAllThreads(int n);

void queueTask(Task* new_task)
{
boost::unique_lock<boost::mutex> lock(mutex_);
task_queue_.push(new_task);
cond_.notify_one(); // разбудить один из потоков пула
}


private:

void thread()
{
boost::unique_lock<boost::mutex> locker(mutex_);

for(;)
{
// ожидание задачи
while (task_queue_.empty())
cond_.wait(locker);

// выполнение задачи
Task* task = task_queue_.front();
task_queue_.pop();

locker.unlock();
task->execute();
delete task;
locker.lock();
}
}

boost::mutex mutex_;
boost::condition_variable cond_;

std::queue<Task*> task_queue_;

};


int main()
{
ThreadPool pool;

pool.startAllThreads(3);

for(;)
{
Task* new_task = askUserForTask(); // здесь основной поток блокируется на ожидании ввода
pool.queueTask(new_task);
}
}
{
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.