Форум программистов, компьютерный форум CyberForum.ru

Использование несколькими потоками одной функции - C++

Восстановить пароль Регистрация
 
Александр Макед
0 / 0 / 0
Регистрация: 23.03.2013
Сообщений: 22
31.08.2013, 21:07     Использование несколькими потоками одной функции #1
Есть вот такая функция для =которую выполняют рабочие потоки
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
DWORD WINAPI ThreadFunc(void *pV)
{   
    Data* pD = (Data*)pV;
    int n, iSleeping(0), MaxCountInConteiner(0);
    n = iGlobal++;
    cout<<"\ntut j:"<<n<<endl<<endl;
 
    for(;;)
    {
        WaitForSingleObject(CreEvent, INFINITE);
        if(pD->Flag == true){
            pD->CountElement = MaxCountInConteiner;
            cout<<"=)threadFUNC="<<n<<endl;
            SetEvent(hArEvent[n]);
            return 0;
        }
        
        if(pD->iDeq.size() < pD->LimitsToEnter){
            ++MaxCountInConteiner;
            pD->iDeq.push_back(n*100);
            
            if(pD->iDeq.front() != n*100)//если добавил не этот поток
            {
                cout<<"aaaaaaaaaa"<<endl;
                --MaxCountInConteiner;
                pD->iDeq.pop_front();
            }
        }
        else
        {
            cout<<"bbbbbbbbbbbb"<<endl;
            pD->iDeq.push_back(n*100);
            pD->iDeq.pop_front();
        }
        cout<<"n="<<n<<endl;
        SetEvent(CreEvent);
        iSleeping = rand()%300;
        Sleep(iSleeping/*/pD->CountThread*/);//если не спать то завершается до вывода статистики
    }
Потоки запускаются из главного потока-предка. который создается и запускается в main.
при длительной работе(множестве вставок/удалений)(потоков 50), вылитает.
Вопрос почему?
И вообще= коректно ли давать множеству потоков одну и ту же рабочую функцию?
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
31.08.2013, 21:07     Использование несколькими потоками одной функции
Посмотрите здесь:

Использование одной переменной C++
ввод данных в функции конструктора и использование их в перегруженой функции C++
C++ можно ли в с++ вызвать переменную из одной функции в другую т.е. мы переменну задали в одной функции а использовали в другой... и как это реализовать?
C++ Использование структуры с двумя полями и одной функцией
Дано строка, состоящая из русских слов, разделенных пробелами (одним или несколькими). ​​Определить количество слов, которые заканчиваются одной и той C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Убежденный
Системный программист
 Аватар для Убежденный
14219 / 6234 / 988
Регистрация: 02.05.2013
Сообщений: 10,391
Завершенные тесты: 1
31.08.2013, 21:26     Использование несколькими потоками одной функции #2
Цитата Сообщение от Александр Макед Посмотреть сообщение
при длительной работе(множестве вставок/удалений)(потоков 50), вылитает.
Вопрос почему?
Нужен полный код.

Цитата Сообщение от Александр Макед Посмотреть сообщение
И вообще= коректно ли давать множеству потоков одну и ту же рабочую функцию?
Корректно.
Каждый поток получит свою копию локальных переменных функции.
Александр Макед
0 / 0 / 0
Регистрация: 23.03.2013
Сообщений: 22
31.08.2013, 22:03  [ТС]     Использование несколькими потоками одной функции #3
Цитата Сообщение от Убежденный Посмотреть сообщение
Нужен полный код.



Корректно.
Каждый поток получит свою копию локальных переменных функции.
вот весь код

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
CRITICAL_SECTION main_g_cs;
static HANDLE MainThread, CreThread ,hArThread[MAX_THREAD];
static HANDLE MainEvent,    CreEvent ,hArEvent[MAX_THREAD];
static int iGlobal(0) ;
 
DWORD WINAPI CreateThreadFunc(void *pV);
DWORD WINAPI ThreadFunc(void *pV);
 
struct Data
{
    bool Flag;
    deque<int> iDeq;
    unsigned int LimitsToEnter;
    unsigned int CountThread;
    /*static */unsigned int CountElement;
    int iAr[MAX_THREAD];
    
    Data():Flag(false), CountElement(0){}
};
 
class Rabotnik
{
    //зделать чтоб нельзя было создать боьше 1 обьекта
    Data* pData;
    static Rabotnik* Self;
 
    Rabotnik()
    {
        pData = new Data();
    }
    ~Rabotnik()
    {
        delete pData;
    }
    Rabotnik& operator=(const Rabotnik);
    Rabotnik(const Rabotnik& Root);
public:
    static Rabotnik* CreateFunction()
    {
        if(!Self)
            Self = new Rabotnik();
 
        return Self;
    }
    static void DeleteFunction()
    {
        if(Self)
            delete Self;
    }
    int MainThreadFunc()
    {
            //InitializeCriticalSection(&main_g_cs);
        MainEvent = CreateEvent(0, TRUE, FALSE, L"CreateEventForMainThread1");
        DWORD dw = 0;
        MainThread = CreateThread(0, 0, CreateThreadFunc, pData, /*CREATE_SUSPENDED*/0, &dw);
        WaitForSingleObject(MainEvent, INFINITE);
            //EnterCriticalSection(&main_g_cs);
 
        cout<<"=)MAINTHREADfunc"<<endl;
        
            //DeleteCriticalSection(&main_g_cs);
 
        return 0;
    }
};
 
Rabotnik* Rabotnik::Self = 0;
 
 
int _tmain(int argc, _TCHAR* argv[])
{
    {
    Rabotnik *pRab(Rabotnik::CreateFunction());
    pRab->MainThreadFunc();
    Rabotnik::DeleteFunction();
    }
    if(_CrtDumpMemoryLeaks())
        cout<<"MemoryLeaks!!!"<<endl;
 
    cout<<"=((main"<<endl;
 
    return 0;
}
 
 
DWORD WINAPI CreateThreadFunc(void *pV)
{   
    Data* pD = (Data*)pV;
 
    int CountThreadCreate, LimitsToenter;
    CreEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
 
    do{
        cout<<"Enter count of thread (amount must be greater than 0): "<<endl;
        cin>>CountThreadCreate;
    }
    while(1 > CountThreadCreate || CountThreadCreate > 64);
    pD->CountThread = CountThreadCreate;
 
    do{
        cout<<"Enter Limits of entering (amount must be greater than 0): "<<endl;
        cin>>LimitsToenter;
        
    }while(LimitsToenter < 1);
    pD->LimitsToEnter = LimitsToenter;
 
    //DWORD dw2 = 0;
    for(int i = 0; i < pD->CountThread; i++)
    {
        hArEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
        pD->iAr[i] = i; 
        hArThread[i] = CreateThread(0, 0, ThreadFunc, pD, /*CREATE_SUSPENDED*/0, /*&dw2*/0);
    }
 
    char CoEx('c'); 
    cout<<"For stop press any key..."<<endl;
    SetEvent(CreEvent);
    CoEx = _getch();
 
    if(CoEx != 'c')
        pD->Flag = true;
 
    WaitForMultipleObjects(pD->CountThread, hArEvent, TRUE, INFINITE);
    
    deque<int>::iterator iTerDeq;   
    if(pD->iDeq.size() > 0)
    {
        cout<<"Max element in conteiner == "<<pD->CountElement<<"==!!"<<endl<<endl;
        for(iTerDeq = pD->iDeq.begin(); iTerDeq  != pD->iDeq.end(); iTerDeq++)
            cout<<"*iTerDeq = "<<*iTerDeq<<endl;
    }
    else
        cout<<"Conteiner is empty =("<<endl;    
    
    SetEvent(MainEvent);
    //LeaveCriticalSection(&main_g_cs);
    cout<<"=))))))CreateThread"<<endl;
    return 0;
}
 
DWORD WINAPI ThreadFunc(void *pV)
{   
    Data* pD = (Data*)pV;
    int n, iSleeping(0), MaxCountInConteiner(0);
    n = iGlobal++;
    cout<<"\ntut j:"<<n<<endl<<endl;
 
    for(;;)
    {
        WaitForSingleObject(CreEvent, INFINITE);
        if(pD->Flag == true){
            pD->CountElement = MaxCountInConteiner;
            cout<<"=)threadFUNC="<<n<<endl;
            SetEvent(hArEvent[n]);
            return 0;
        }
        
        if(pD->iDeq.size() < pD->LimitsToEnter){
            ++MaxCountInConteiner;
            pD->iDeq.push_back(n*100);
            
            if(pD->iDeq.front() != n*100)//если добавил не этот поток
            {
                cout<<"aaaaaaaaaa"<<endl;
                --MaxCountInConteiner;
                pD->iDeq.pop_front();
            }
        }
        else
        {
            cout<<"bbbbbbbbbbbb"<<endl;
            pD->iDeq.push_back(n*100);
            pD->iDeq.pop_front();
        }
        cout<<"n="<<n<<endl;
        SetEvent(CreEvent);
        iSleeping = rand()%300;
        Sleep(iSleeping/*/pD->CountThread*/);//если не спать то завершается до вывода статистики
    }
    return 0;
}
Убежденный
Системный программист
 Аватар для Убежденный
14219 / 6234 / 988
Регистрация: 02.05.2013
Сообщений: 10,391
Завершенные тесты: 1
31.08.2013, 22:18     Использование несколькими потоками одной функции #4
Код, конечно, не сахар. Можете описать, в чем смысл данных вычислений ?
Александр Макед
0 / 0 / 0
Регистрация: 23.03.2013
Сообщений: 22
31.08.2013, 22:22  [ТС]     Использование несколькими потоками одной функции #5
Цитата Сообщение от Убежденный Посмотреть сообщение
Код, конечно, не сахар. Можете описать, в чем смысл данных вычислений ?
Основной поток запускает X (1 < X <= 64) вспомогательных потоков. X - первый параметр командной строки.
Каждый из вспомогательных потоков бесконечно (с некоторой изменяющейся задержкой) добавляет в общий контейнер свой элемент.
При этом он удаляет самый старый элемент в случае, если тот был добавлен другим потоком или если количество элементов в контейнере > Y ( Y - второй параметр командной строки).
Основной поток ожидает любого ввода из командной строки (например нажатия на Enter ).
После получения ввода из командной строки основной поток должен распечатать статистику и максимальное зарегистрированное количество элементов в контейнере.

командную строку пока не подключал=простой ввод с консоли.
Dmitriy_M
1297 / 1178 / 106
Регистрация: 20.03.2009
Сообщений: 4,214
Записей в блоге: 11
31.08.2013, 22:34     Использование несколькими потоками одной функции #6
Александр Макед, ты в курсе что rand нельзя использовать из несколько потоков?
Цитата Сообщение от Убежденный Посмотреть сообщение
Корректно.
только в случае отсутствия побочных эффектов.
Убежденный
Системный программист
 Аватар для Убежденный
14219 / 6234 / 988
Регистрация: 02.05.2013
Сообщений: 10,391
Завершенные тесты: 1
31.08.2013, 22:36     Использование несколькими потоками одной функции #7
Цитата Сообщение от Dmitriy_M Посмотреть сообщение
rand нельзя использовать из несколько потоков?
Это еще почему ?
Александр Макед
0 / 0 / 0
Регистрация: 23.03.2013
Сообщений: 22
31.08.2013, 22:58  [ТС]     Использование несколькими потоками одной функции #8
проблема с контейнером, он все время ругается на итератор- то его инкрементить нельзя , то декрементить то разименовывать. если в функции порожденных потоков работу с контейнером убрать, работает аж бегом, а так не хочет.
Убежденный
Системный программист
 Аватар для Убежденный
14219 / 6234 / 988
Регистрация: 02.05.2013
Сообщений: 10,391
Завершенные тесты: 1
31.08.2013, 23:20     Использование несколькими потоками одной функции #9
А нельзя ли это все переписать в более простом стиле ?
Например (ПСЕВДОКОД) :
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int main()
{
    control Control; // Некий класс для управления потоками.
 
    HANDLE *pThreadHandles = new HANDLE[NumThreads];
 
    // Создаем столько-то потоков и синхронно стартуем их.
 
    for (int i = 0; i < NumThreads; ++i)
    {
        pThreadHandles[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, &Control, 0, NULL);
    }
 
    Control.start();
 
    // Ждем команды пользователя и даем потокам команду "завершить".
    _getch();
    Control.stop();
 
    // Ожидание завершения.
 
    WaitForMultipleObjects(NumThreads, pThreadHandles, TRUE, INFINITE); 
 
    for (int i = 0; i < NumThreads; ++i)
    {
        CloseHandle(pThreadHandles[i]);
    }
 
    delete [] pThreadHandles;
 
    // Здесь обработка результатов.
    // ...
 
    return 0;
}


Ну а функция потока могла быть примерно такой:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
unsigned int _stdcall ThreadProc(void *pParam)
{
    control *pCtrl = (control *)pParam;
    DWORD const ThreadId = GetCurrentThreadId(); // Получаем ID текущего потока.
 
    pCtrl->wait_for_start(); // Ждем сигнала "старт".
 
    for (;;)
    {
        if (false != pCtrl->check_for_quit()) // Проверка на завершение.
        {
            break;
        }
 
        Sleep(rand() % 300); // Рандомная задержка.
        
        // Синхронизация доступа к deque (например, на основе CRITICAL_SECTION или spin-lock).
 
        scoped_lock_t ScopedLock(pCtrl->SyncObject);
 
        // Помещаем новый элемент в контейнер.
        item NewItem = {ThreadId};
        pCtrl->Deque.push_back(NewItem);
        
        // Проверка на переполнение.
 
        if (pCtrl->Deque.size() > MaxItems)
        {
            pCtrl->Deque.pop_front();
        }
        
        // Проверка на то, что элемент в голове был вставлен другим потоком.
 
        else if (false == pCtrl->Deque.empty())
        {
            if (ThreadId != pCtrl->Deque.front().ThreadId)
            {
                pCtrl->Deque.pop_front();
            }
        }        
    }
 
    return 0;
}
Александр Макед
0 / 0 / 0
Регистрация: 23.03.2013
Сообщений: 22
31.08.2013, 23:44  [ТС]     Использование несколькими потоками одной функции #10
Цитата Сообщение от Убежденный Посмотреть сообщение
А нельзя ли это все переписать в более простом стиле ?
Например (ПСЕВДОКОД) :
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int main()
{
    control Control; // Некий класс для управления потоками.
 
    HANDLE *pThreadHandles = new HANDLE[NumThreads];
 
    // Создаем столько-то потоков и синхронно стартуем их.
 
    for (int i = 0; i < NumThreads; ++i)
    {
        pThreadHandles[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, &Control, 0, NULL);
    }
 
    Control.start();
 
    // Ждем команды пользователя и даем потокам команду "завершить".
    _getch();
    Control.stop();
 
    // Ожидание завершения.
 
    WaitForMultipleObjects(NumThreads, pThreadHandles, TRUE, INFINITE); 
 
    for (int i = 0; i < NumThreads; ++i)
    {
        CloseHandle(pThreadHandles[i]);
    }
 
    delete [] pThreadHandles;
 
    // Здесь обработка результатов.
    // ...
 
    return 0;
}


Ну а функция потока могла быть примерно такой:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
unsigned int _stdcall ThreadProc(void *pParam)
{
    control *pCtrl = (control *)pParam;
    DWORD const ThreadId = GetCurrentThreadId(); // Получаем ID текущего потока.
 
    pCtrl->wait_for_start(); // Ждем сигнала "старт".
 
    for (;;)
    {
        if (false != pCtrl->check_for_quit()) // Проверка на завершение.
        {
            break;
        }
 
        Sleep(rand() % 300); // Рандомная задержка.
        
        // Синхронизация доступа к deque (например, на основе CRITICAL_SECTION или spin-lock).
 
        scoped_lock_t ScopedLock(pCtrl->SyncObject);
 
        // Помещаем новый элемент в контейнер.
        item NewItem = {ThreadId};
        pCtrl->Deque.push_back(NewItem);
        
        // Проверка на переполнение.
 
        if (pCtrl->Deque.size() > MaxItems)
        {
            pCtrl->Deque.pop_front();
        }
        
        // Проверка на то, что элемент в голове был вставлен другим потоком.
 
        else if (false == pCtrl->Deque.empty())
        {
            if (ThreadId != pCtrl->Deque.front().ThreadId)
            {
                pCtrl->Deque.pop_front();
            }
        }        
    }
 
    return 0;
}
ну в общем можно и так, только ,как я понимаю, так будет медленнее работать, так как переходы в методы класса жрут ресурсы процессора.
но в любом случае спасибо, поменяю малость дизайн, а там видно будет.
Dmitriy_M
1297 / 1178 / 106
Регистрация: 20.03.2009
Сообщений: 4,214
Записей в блоге: 11
31.08.2013, 23:49     Использование несколькими потоками одной функции #11
Цитата Сообщение от Убежденный Посмотреть сообщение
Это еще почему ?
Потому что не thread-safe.
Описание с http://www.cplusplus.com/
The function accesses and modifies internal state objects, which may cause data races with concurrent calls to rand or srand.

Some libraries provide an alternative function that explicitly avoids this kind of data race: rand_r (non-portable).

C++ library implementations are allowed to guarantee no data races for calling this function.
В POSIX об этом сказано явно
The function rand() is not reentrant or thread-safe, since it uses hidden state that is modified on each call. This might just be the seed value to be used by the next call, or it might be something more elaborate. In order to get reproducible behavior in a threaded application, this state must be made explicit; this can be done using the reentrant function rand_r().
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
01.09.2013, 10:09     Использование несколькими потоками одной функции
Еще ссылки по теме:

Подскажите библиотеки, функции по управлению потоками C++
Применение переменных одной функции в другой функции C++
C++ Функции работы с потоками

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Убежденный
Системный программист
 Аватар для Убежденный
14219 / 6234 / 988
Регистрация: 02.05.2013
Сообщений: 10,391
Завершенные тесты: 1
01.09.2013, 10:09     Использование несколькими потоками одной функции #12
В Visual C++ стандартные функции уже много лет как thread-safe.
Library Support for Multithreading
The Multithread C Libraries: LIBCMT.LIB and MSVCRT.LIB

The support library LIBCMT.LIB is a reentrant library for creating multithread programs.
The MSVCRT.LIB library, which calls code in the shared MSVCRT70.DLL, is also reentrant.
Вот, к примеру, что написано про функцию strtok:
strtok, _strtok_l, wcstok, _wcstok_l, _mbstok, _mbstok_l
Each function uses a thread-local static variable for parsing the string into tokens.
Therefore, multiple threads can simultaneously call these functions without undesired effects.
However, within a single thread, interleaving calls to one of these functions is highly likely to
produce data corruption and inaccurate results. When parsing multiple strings, finish parsing one
string before parsing the next.
А это про srand:
srand
The srand function sets the starting point for generating a series of pseudorandom integers in the current thread.
У каждого потока своя копия "состояния" (через TLS), поэтому в Visual C++ такие
функции, как rand, можно без всяких опасений использовать в разных потоках.

Цитата Сообщение от Александр Макед Посмотреть сообщение
так будет медленнее работать, так как переходы в методы класса жрут ресурсы процессора.
Гадание на кофейной гуще.
Переход в метод класса - это, грубо говоря, один дополнительный call.
И код метода запросто может быть встроен по месту вызова. Так что каких-то
дополнительных накладных расходов там ноль без палочки. А вот ожидание на
объектах ядра (event) - вот это действительно дорогая операция, особенно для
сценария с 50 потоками.
Yandex
Объявления
01.09.2013, 10:09     Использование несколькими потоками одной функции
Ответ Создать тему
Опции темы

Текущее время: 01:46. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru