У меня есть серверная программа, которая отправляет данные клиентов другому, который их обрабатывает. Но поскольку выполнение функции может занять много времени и не позволяет программе работать с данными других клиентов, я хочу одновременно выполнять ту же функцию над данными других клиентов, не используя pthreads и не создавая процессы.
Я пытался создать что-то, что может сделать это, но это уродливо и, конечно, не лучшим образом. Вот мой код:
#include <stdio.h>
#include <string.h>
#include <vector>
struct userdata {
int rt; //The 'func' function uses it to resume work where it returned the last time
int len; //The length of 'data'
char data[16384]; //Client's data
};
int func(userdata *ud)
{
//The gotos are here to jump to the code where the function returned the last time
if(ud->rt==1)goto P1;
if(ud->rt==2)goto P2;
ud->len=0;
//Code to calculate the length of 'data'
while(ud->data[ud->len]!=0)
{
ud->rt=1; //Set to 1 to indicate where to resume execution the next time we will process the same data
return 0;
P1:
ud->len++;
}
// Work
ud->rt=2;
return 0;
P2:
// Work
return 1;
}
int main(int argc, char *argv[])
{
userdata ud;
memset(ud.data,1,16383);
ud.data[16383]=0;
std::vector<userdata> vec;
for(int i=0;i!=200;i++)vec.push_back(ud); //Add 200 times the structure in the vector
unsigned int Sz=vec.size(); //I'll use Sz in the for loop to avoid calling size()
bool Loop=true;
do
{
for(int i=0;i!=Sz;i++)
{
if( func(&vec.at(i))==1) //If the function returned 1 this means that there's no more work to do
{
printf("str length = %i\n",vec.at(i).len); //Display the result
vec.erase(vec.begin() + i); //Remove element from vector because there's no more work to do
i--, Sz--; //Decrement Sz (size of the vector) and i (vector index) to avoid out_of_range exception
if(Sz==0)Loop=false; //If there are no elements in the vector, leave Loop
}
}
}
while(Loop);
return 0;
}
Проблема здесь в том, что это не параллельное выполнение, я должен поместить в структуру переменные, которые должны быть восстановлены до их последнего состояния, и это может занять много времени, когда вектор содержит тысячи элементов.
Не пытайтесь написать свою собственную универсальную библиотеку потоков, это будет дороже, чем pthreads. Вместо этого разделите проблему, используя знания вашей конкретной проблемы. Найти место, где это делает смысл к
Я предполагаю, что у вас уже есть основной цикл, который просто вызывает select
/poll
/epoll
кучка. Если у вас еще нет тайм-аута, начните использовать его, чтобы вы могли хранить набор дополнительных, синхронизированных и даже триггеров в куче.
Затем в каждом вычислении после определенного числа итераций останавливайте расписание для вызова функции + данные. Использовать ток время (в конце, в отличие от времени начала текущего события таймера). Предполагая, что время действительно истекло, диспетчер таймера сначала завершит все вычисления для самого старого тика, а затем перейдет к следующему шагу нового тика (приблизительно в циклическом планировщике). Обратите внимание, что вы делаете не Вы хотите, чтобы размер среза был слишком маленьким, иначе издержки на переключение задач начнут доминировать.
В зависимости от вашего набора проблем, вы можете или не можете добавить логику для отмены любых будущих таймеров, если умирает связанный клиент.
Не пытайтесь изобретать велосипед. Механизм заправки сложен. Все, что вы делаете, будет работать хуже и медленнее, чем ваша система.
Копирование данных в и из структуры данных, используемой функцией, является странным подходом. Где бы вы это положили? Обычный подход к многопоточности состоит в том, чтобы иметь по одному стеку на каждый поток (каждый поток имеет как стек вызовов, так и стек данных). Каждый поток выполняет один экземпляр функции, и если в куче выделяется память, каждый экземпляр проблемы сохраняется в разных местах кучи (поэтому стек и регистр в каждом потоке будут содержать указатели на разные области кучи).