Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Многопоточное програмирование.doc
Скачиваний:
3
Добавлен:
14.11.2019
Размер:
332.8 Кб
Скачать

3.1. Example1: работа с памятью

Для начала поговорим о подводных камнях C++ применительно к MT программированию. На первый взгляд может показаться, что написанные на C++ потоки, не изменяющие одни и те же данные одновременно, могут работать абсолютно независимо и параллельно. К сожалению, это не совсем так: основной проблемой является стандартный распределитель памяти, т.е. операторы new/delete. Суть проблемы в том, что приложение получает свободные блоки памяти от системы и выдает их части потокам по требованию, т.е. в процессе своей работы потоки вынуждены изменять одни и те же структуры данных (глобальные цепочки свободных блоков) и, следовательно, синхронизация их работы неизбежна.

Прямым решением данной проблемы является повсеместное явное использование собственного распределителя памяти, кэширующего полученные от глобального распределителя блоки. Т.е. своего объекта mem_pool для каждого отдельного потока (как минимум). Конечно, с точки зрения удобства кодирования повсеместное мелькание ссылок mem_pool& трудно назвать приемлемым. Разберем следующий пример:

example1/main.cpp

#include <list>

#include <vector>

#include <stdio.h>

#include <time.h>

#include <ders/stl_alloc.hpp>

#include <ders/thread.hpp>

using namespace std;

using namespace ders;

const int N=1000;

const int M=10000;

void start_std(void*)

{

list<int> lst;

for (int i=0; i<N; i++) {

for (int j=0; j<M; j++) lst.push_back(j);

for (int j=0; j<M; j++) lst.pop_front();

}

}

void start_ders(void*)

{

mem_pool mp;

stl_allocator<int> alloc(mp);

list<int, stl_allocator<int> > lst(alloc);

for (int i=0; i<N; i++) {

for (int j=0; j<M; j++) lst.push_back(j);

for (int j=0; j<M; j++) lst.pop_front();

}

}

int main(int argc, char** argv)

{

if (argc!=3) {

m1:

fprintf(stderr, "main num_threads std|ders");

return 1;

}

int numThr=atoi(argv[1]);

if ( !(numThr>=1 && numThr<=100) ) {

fprintf(stderr, "num_threads must be in [1, 100]");

return 1;

}

void (*start)(void*);

if (strcmp(argv[2], "std")==0) start=start_std;

else if (strcmp(argv[2], "ders")==0) start=start_ders;

else goto m1;

clock_t c1=clock();

mem_pool mp;

vector<sh_thread> vthr;

for (int i=0; i<numThr; i++) vthr.push_back(new_thread(mp, start, 0));

for (int i=0; i<numThr; i++) vthr[i]->join();

clock_t c2=clock();

printf("%d\t%d\t%s\n", numThr, int(c2-c1), argv[2]);

return 0;

}

Программа запускает заданное в командной строке количество потоков и в каждом из них выполнят фиксированное количество вставок/удалений элементов в стандартный список. Отличие функции start_ders состоит в том, что вместо стандартного аллокатора по умолчанию lst использует аллокатор на основе mem_pool.