Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.191.132.194] |
|
Сообщ.
#1
,
|
|
|
Всем привет.
Пишу программку на Си только под Windows.Она ищет все файлы на диске по маске ,"*.тхт"например,найдя файл,создаёт поток pthread в котором вызывает функцию и передаёт ей в ,качестве аргумента,найденный файл и так далее,пока не достигнет заданного ограничения потоком. По достижению,ждёт завершения потоков и продолжает передачу новых найденных файлов. Функция в потоке,в свою очередь,работает с этими файлами,а точнее копирует их на другой диск открывая файл и читая байты в заданный буфер BYTE,а от туда в новый файл. Всё хорошо,когда файлов либо один,либо с десяток,при условии,что они не большого размера(который буфер может прочитать за один цикл). Но если их много,в одном из потоков,цикл чтения и записи становится бесконечным,соответственно,программа стоит на месте(не зависает и не выдаёт ошибку),цикл вращается - поток не завершается.При этом программа перестаёт читать и писать, нагрузка на память вовсе пропадает,а ЦП наоборот,сильно возрастает,за счёт огромного кол-ва бесконечных циклов. Даже если файл большой,но он один(= один поток),чтение и запись проходит успешно,поток завершается. file2 новый файл(копия file1 на другом диске) который я создаю.Да,в этом примере вывод написан не корректно. Получается,что открывается для записи один и тот же файл.Но это не так.Просто я упростил этот пример.Прошу прощения за путаницу. Каждому файлу,свой новый файл. Смысл в том,что программа ищет на всём диске все файлы txt и копирует их на другой диск. 1 файл = 1 поток pthread. Ограничение в 4 потока одновременно.Когда создаётся 4 потока, вызывается pthread_join для каждого созданного потока. Потом счётчик обнуляется и всё по новой,только уже с новыми файлами. В чём проблема понять никак не могу. Помогите найти ошибку,пожалуйста. Код работает в 2 случаях: 1.Если ограничить кол-во потоков в 1,соответственно копирование происходит в 1 поток и это не то,что нужно. 2.Если убрать цикл while(!feof (in)), но в таком случае нужно целиком грузить файл в память,что не выход из положения,так как это прототип программы и ,вполне возможно,нужно будет копировать увесистые видео файлы и jpeg,ну или ещё что. memory(1) - возвращает целое число равное проценту загруженности памяти. find_file - целое число типа int,счётчик файлов. #include <stdio.h> #include <stdlib.h> #include <errno.h> void *CopyMyFile(void *file) { FILE *in = fopen(file,"rb"), *out = fopen(file2,"wb"); if (in == NULL || out == NULL) { perror (file); return NULL; } rewind(out); unsigned long int buf = 1e+7; BYTE *buffer = 0; if ((buffer = (BYTE *)malloc(buf *sizeof(BYTE)))==NULL) { printf("err buffer %s\r\n", strerror(errno)); fclose(in); fclose(out); return NULL; } while(!feof (in)) //тут возникает бесконечный цикл,при работе в более чем один поток { DWORD Len = fread(buffer, sizeof(BYTE), buf, in); fwrite(buffer, sizeof(BYTE), Len, out); } free(buffer); fclose(in); fclose(out); return NULL; } BOOL logFile(char *file) { if( memory(1) < 86 && find_file < 4 ) { pthread_create(&threadslogFile[find_file], NULL, CopyMyFile, (void *)file); find_file++; } else { for(int j = 0; j < find_file; j++) { pthread_join(threadslogFile[j], NULL); } find_file = 0; pthread_create(&threadslogFile[find_file], NULL, CopyMyFile, (void *)file); find_file++; } return TRUE; } |
Сообщ.
#2
,
|
|
|
Ввиду того, что я не нашёл критических ошибок в коде и, построив на его основе пример, не обнаружил проблемы, могу заключить, что что-то из (или даже несколько):
Если последнее, то зацикливание на while(!feof()) объясняется естественно: это не конец файла, но и прочитать ничего нельзя из-за состояния ошибки. Добавь ferror() в цикл, узнаешь наверняка. |
Сообщ.
#3
,
|
|
|
Цитата Qraizer @ [*]приложение не многопоточное; проверь, что используешь что-то типа -MT компилятору; если нет, то stdio.h тупо попадает в race condition; Спасибо за ответ. Компилирую MinGW gcc main.c -o copy -static -s -Wall -Wextra -Werror -W -lpthread |
Сообщ.
#4
,
|
|
|
Цитата Sherman @ Добавил printf для вывода текста строки из функции потока.Не один файл не повторяется.И всё же проблема осталась. Как будто в цикле не может определить конец файла.Но при этом не происходит не чтения не запись. Скорость чтения с диска 0 . Прочитывает пачку файлов и на 1-2х из низ зацикливается. |
Сообщ.
#5
,
|
|
|
Вот не уверен, что мингву научили позикс-ниткам. До сих пор был инфа, что полноценной поддержки pthread ещё нет, и сами её контрибуторы рекомендуют VS-ную реализацию типа там _beginthreadex() etc, т.к. мигва использует обычные визуальные библиотеки. Попробуй-таки вот эту реализацию. У меня по крайней мере она собралась влёгкую. Для VC2017, правда, но для MinGW там тоже есть сценарии конфигурации и сборки.
|
Сообщ.
#6
,
|
|
|
Цитата Qraizer @ Так без цикла whie потоки работают. Ставлю ограничение на 100 потоков,работает на ура. Но это не вариант,память не резиновая)) Пробовал с CreatyThread WIN API,было то же самое. |
Сообщ.
#7
,
|
|
|
Цитата Sherman @ Получается,что открывается для записи один и тот же файл.Но это не так.Просто я упростил этот пример.Прошу прощения за путаницу. Покажи полный код, этот что-то слишком упрощён. |
Сообщ.
#8
,
|
|
|
Цитата Sherman @ Это лишь значит, что ОС успешно избегает race condition в своих функциях ReadFile()/WriteFile(). Ну, как бы сомнений и не было. При единственном fread()/fwrite() на каждый файл, возможно, что и stdio не попадёт на race condition в своём коде. Пока всё говорит о том, что к твоей программе линкуется немногопоточная RTL, что вызывает коллизии общих данных библиотеки ввода/вывода. Ну или почему-то происходят ошибки чтения, о которых ты не знаешь, потому что не контролируешь ferror()Так без цикла whie потоки работают. Добавлено Цитата Sherman @ К слову ещё. Не уверен, что -lpthread достаточно. МинГВу особо и не юзал, а в gcc это означает лишь линковку с библиотекой pthread. Ну ок, прилинкуется, будут тебе потоки. Что-то мне подсказывает, что это не означает автоматически, что вместо однопоточной libstdc должна использоваться многопоточная. CreateThread() и K° тебе не помогут, они тут совершенно не причём. Просто подумай капельку: у тебя есть две нитки, которые вдруг внезапно одновременно решили сделать тебе printf("Hello, multithreaded world!\n"). Представил? Усугублю: представь, что каждая из ниток выполнила запись в свой уникальный файл, но у одной из них тут внезапно эррор; какое значение будет у errno? Пробовал с CreatyThread WIN API,было то же самое. |
Сообщ.
#9
,
|
|
|
Qraizer, на сколько я знаю - MinGW64 поставляется в 2-х видах реализации потоков: POSIX и winpthreads. Первая более тормозная, но обеспечивает на 100% все фичи многопоточности, декларированной в С++11 и выше. Вторая - типа ускоренная, но все фичи многопоточности С++11 не реализует. А вот на счет голого Си - инфы мало. Просьба к тебе, как к гуру Си и иже с ним... Разберись и проясни ситуацию. Тебе это обернется более "меньшей кровью" ввиду большего опыта. А профит для форума и форумчан будет просто адовый! Плис-плис!!!
|
Сообщ.
#10
,
|
|
|
Цитата Qraizer @ но у одной из них тут внезапно эррор; какое значение будет у errno? Я совсем не давно начал заниматься программированием и затрудняюсь вам ответить. Добавлено Цитата Олег М @ Покажи полный код, этот что-то слишком упрощён. Хорошо,но сейчас,ввиду множественных переработок,код такой,что и я не всегда понимаю. Дайте немного времени,я перепишу в более вразуметельной форме и выложу. |
Сообщ.
#11
,
|
|
|
Компиляция: gcc copy.c -o copy -static -s -Wall -Wextra -Werror -W -lpthread
Вот полный код: #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <sys/stat.h> #include <assert.h> #include <signal.h> #include <unistd.h> #include <pthread.h> #ifndef MAX_PATH #define MAX_PATH 1024 #endif #define BLOCK_SIZE 3.2e+7 #define THREADS_NUMBER 10 int find_file = 0, drive = 0; char DriveS[MAX_PATH] = { 0 }; char TypeFile[MAX_PATH] = { 0 }; char NameFile[MAX_PATH] = { 0 }; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond; pthread_t *threadslogFile = { 0 }; pthread_t *threadsBuffer = { 0 }; void * SearchFiles(void * lpszFile); int main(void) { //здесь в консоль вводим имя диска,на который будем копировать //и тип искомых файлов. //пока статично задаю. strcpy(DriveS, "E:\\"); strcpy(TypeFile, "txt"); threadslogFile = (pthread_t*) malloc(100 * sizeof(pthread_t)); assert(threadslogFile); threadsBuffer = (pthread_t*) malloc(32 * sizeof(pthread_t)); assert(threadsBuffer); char buf[120] = { 0 }; char drives[32][10] = { 0 }; GetLogicalDriveStringsA(sizeof(buf), buf); for (char *s = buf; *s; s += strlen(s) + 1) { if (!strcmp(s, DriveS) == 0) { strcpy(drives[drive], s); drive++; } } for(int i = 0; i < drive; i++) pthread_create(&threadsBuffer[i], NULL, SearchFiles, (void*)drives[i]); for(int i = 0; i < drive; i++) pthread_join(threadsBuffer[i], NULL); for(int j = 0; j < find_file; j++) pthread_join(threadslogFile[j], NULL); free(threadsBuffer); free(threadslogFile); printf("\r\n\r\nend"); getchar(); return 0; } unsigned long long memory(int mod) { MEMORYSTATUS theStatus; ZeroMemory(&theStatus, sizeof(theStatus)); theStatus.dwLength = sizeof(theStatus); GlobalMemoryStatus(&theStatus); if(mod == 1) { return theStatus.dwMemoryLoad; // %load } if(mod == 2) { return (theStatus.dwAvailPhys); // avail memory byte } if(mod == 3) { return (theStatus.dwTotalPhys); // total memory byte } return 0; } unsigned long int fsize(const char *filename) { struct stat st; if (stat(filename, &st) == 0) return st.st_size; return -1; } void * CopyMyFile(void * ffile) { char file[MAX_PATH] = { 0 }; strcpy(file, (CONST char *)ffile); free(ffile); char new_file[MAX_PATH] = { 0 }; strcpy(new_file, DriveS); strcat(new_file, NameFile); find_file++; printf("%s\r\n", NameFile); pthread_mutex_unlock(&lock); FILE *in = fopen(file,"rb"), *out = fopen(new_file,"wb"); if (in == NULL || out == NULL) { perror (file); return NULL; } rewind(out); unsigned long int buf = 0; unsigned long int file_size = fsize(new_file); if(file_size < BLOCK_SIZE) buf = (file_size + 64); else buf = BLOCK_SIZE; BYTE *buffer = 0; if ((buffer = (BYTE *)malloc(buf *sizeof(BYTE)))==NULL) { printf("Buffer %s\r\n", strerror(errno)); fclose(in); fclose(out); return NULL; } while(!feof (in)) //тут возникает бесконечный цикл,при работе в более чем один поток { DWORD Len = fread(buffer, sizeof(BYTE), buf, in); fwrite(buffer, sizeof(BYTE), Len, out); } free(buffer); fclose(in); fclose(out); if ( in ) fclose( in ); if ( out ) fclose( out ); DWORD dwAttrNewFile = GetFileAttributes(file); SetFileAttributes(new_file, dwAttrNewFile); return NULL; } char *CheckType (register const char *s, int c) { char *rtnval = "0"; do { if (*s == c) rtnval = (char*) s; } while (*s++); return (rtnval + 1); } void * SearchFiles(void * lpszFile) { CONST char *lpszFileName = (CONST char *)lpszFile; char path[MAX_PATH] = { 0 }; char temp[MAX_PATH] = { 0 }; strcpy(path, lpszFileName); strcat(path, "*.*"); HANDLE hand; WIN32_FIND_DATA data_file; hand = FindFirstFile(path, &data_file); if (hand != INVALID_HANDLE_VALUE) { do { if (!strncmp(data_file.cFileName, ".", 1) || !strncmp(data_file.cFileName, "..", 2)) { continue; } if (data_file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { strcpy(path, lpszFileName); strcat(path, data_file.cFileName); strcat(path, "\\"); strcpy(temp, path); strlwr(temp); if (!lstrcmp(temp, "c:\\windows\\") == 0) { SearchFiles(path); } } else { char * type = CheckType(data_file.cFileName,'.'); if(strcmp(type, TypeFile) == 0 ) { pthread_mutex_lock(&lock); char *file = (char*)malloc(strlen(lpszFileName) + strlen(data_file.cFileName) + 64); assert(file); strcpy(file, lpszFileName); strcat(file, data_file.cFileName); strcpy(NameFile,data_file.cFileName); if( memory(1) < 86 && find_file < THREADS_NUMBER ) { pthread_create(&threadslogFile[find_file], NULL, &CopyMyFile, (void *)file); } else { for(int j = 0; j < find_file; j++) { pthread_join(threadslogFile[j], NULL); } find_file = 0; pthread_create(&threadslogFile[find_file], NULL, &CopyMyFile, (void *)file); } } } } while (FindNextFile(hand, &data_file) != 0); } FindClose(hand); free(lpszFile); return NULL; } Добавлено Если не затруднит,попробуйте скомпилировать и протестировать. |
Сообщ.
#12
,
|
|
|
Цитата Sherman @ Код работает в 2 случаях: 1.Если ограничить кол-во потоков в 1,соответственно копирование происходит в 1 поток и это не то,что нужно. 2.Если убрать цикл while(!feof (in)), я попробовал сделать то, что и ты, и у меня всё получилось. MinGw студия у меня какая-то очень старая, и текст твой я копировал конечно не весь, а кусками. Число файлов и число потоков можно менять и проблем не возникало. Получилось так: Скрытый текст // -------------------------------------------------------------------------- // // file HelloWorld.cpp 22.12.2012, 05.05.2019 // // -------------------------------------------------------------------------- #include "stdafx.h" #include <pthread.h> // -------------------------------------------------------------------------- using namespace std; // -------------------------------------------------------------------------- #define ERROR_CREATE_THREAD -11 #define ERROR_JOIN_THREAD -12 #define SUCCESS 0 // -------------------------------------------------------------------------- const TCHAR* pSource[] = { _T("1.bmp"), _T("2.bmp"), _T("3.bmp"), _T("4.bmp"), _T("5.bmp"), _T("6.bmp"), _T("7.bmp"), _T("8.bmp"), _T("9.bmp"), _T("10.bmp"), NULL }; const TCHAR* pTarget[] = { _T("d:\\tmp\\20190505\\1.bmp"), _T("d:\\tmp\\20190505\\2.bmp"), _T("d:\\tmp\\20190505\\3.bmp"), _T("d:\\tmp\\20190505\\4.bmp"), _T("d:\\tmp\\20190505\\5.bmp"), _T("d:\\tmp\\20190505\\6.bmp"), _T("d:\\tmp\\20190505\\7.bmp"), _T("d:\\tmp\\20190505\\8.bmp"), _T("d:\\tmp\\20190505\\9.bmp"), _T("d:\\tmp\\20190505\\10.bmp"), NULL }; void* CopyMyFile(void *arg); typedef struct { const TCHAR* pSource; const TCHAR* pTarget; } Files; static const int THREADS = 4; // -------------------------------------------------------------------------- int main(void) { int status=0; int status_addr=0; int iSource=0; for(;;) { Files fdata [THREADS]; pthread_t thread [THREADS]; for(int j=0;j<THREADS;++j) { thread[j].p=NULL; } _tprintf(_T("iSource = %d\n"), iSource); for(int j=0;j<THREADS;++j,++iSource) { if(pSource[iSource]==NULL) break; fdata[j].pSource = pSource[iSource]; fdata[j].pTarget = pTarget[iSource]; status = pthread_create(&thread[j], NULL, CopyMyFile, (void *)&fdata[j]); if(status != SUCCESS) { _tprintf(_T("pthread_create error, status = %d\n"), status); break; } } int iExit=1; for(int j=0;j<THREADS;++j) { if(thread[j].p==NULL) continue; iExit=0; status = pthread_join(thread[j], (void**)&status_addr); if (status != SUCCESS) { _tprintf("pthread_join error, status = %d\n", status); break; } } _tprintf(_T("iExit = %d\n"), iExit); if(iExit) break; } _tsystem(_T("pause")); return 0; } // -------------------------------------------------------------------------- void* CopyMyFile(void *args) { _tprintf(_T("CopyFile: %s to %s\n"), ((Files*) args)->pSource, ((Files*) args)->pTarget); FILE *in = fopen(((Files*) args)->pSource,"rb"); FILE *out = fopen(((Files*) args)->pTarget,"wb"); if(in == NULL) { _tprintf(_T("in == NULL\n")); return NULL;} if(out == NULL) { _tprintf(_T("out == NULL\n")); return NULL;} static const UINT SIZE=16384; BYTE buffer[SIZE]; for(;;) { DWORD Len = fread(buffer, 1, SIZE, in); if(Len==0) break; fwrite(buffer, 1, Len, out); } fclose(in); fclose(out); return SUCCESS; } // -------------------------------------------------------------------------- Ошибки и ляпы тут могут быть, очень детально не проверял. я в таком стиле вообще не работаю. |
Сообщ.
#13
,
|
|
|
Цитата ЫукпШ @ я попробовал сделать то, что и ты, и у меня всё получилось. Спасибо за участие. Ахах if(Len==0) break; Как я не догадался...? Добавил это в свой код,зацикливание исчезло,но появилась другая проблема. Вылетает ошибка,если найденный файл превышает указанное ограничение размера. Файл частично копируется(пару циклов) и вылетает ошибка. Добавил проверку на существование создаваемого файла,на случай дублей. Не помогло. Сейчас функция выглядит так: void * CopyMyFile(void * ffile) { char file[MAX_PATH] = { 0 }; strcpy(file, (CONST char *)ffile); free(ffile); char new_file[MAX_PATH] = { 0 }; strcpy(new_file, DriveS); strcat(new_file, NameFile); find_file++; FILE *ch = fopen(new_file,"rb"); if (ch != NULL) { fclose(ch); pthread_mutex_unlock(&lock); return NULL; } printf("%s\r\n", NameFile); pthread_mutex_unlock(&lock); FILE *in = fopen(file,"rb"), *out = fopen(new_file,"wb"); if (in == NULL || out == NULL) { perror (file); return NULL; } rewind(out); unsigned long int buf = 0; unsigned long int file_size = fsize(file); if(file_size < 16384000) buf = (file_size + 64); else buf = 16384000; BYTE *buffer = 0; if ((buffer = (BYTE *)malloc(buf *sizeof(BYTE)))==NULL) { printf("Buffer %s\r\n", strerror(errno)); fclose(in); fclose(out); return NULL; } while(!feof(in)) //тут возникает бесконечный цикл,при работе в более чем один поток { DWORD Len = fread(buffer, sizeof(BYTE), buf, in); if(Len==0) break; fwrite(buffer, sizeof(BYTE), Len, out); } free(buffer); fclose(in); fclose(out); return NULL; } |
Сообщ.
#14
,
|
|
|
Цитата Sherman @ Вылетает ошибка,если найденный файл превышает указанное ограничение размера. В моей сборке это, как бы, не влияет. Вот так тоже устойчиво работает: Скрытый текст void* CopyMyFile(void *args) { _tprintf(_T("CopyFile: %s to %s\n"), ((Files*) args)->pSource, ((Files*) args)->pTarget); FILE *in = _tfopen(((Files*) args)->pSource,_T("rb")); FILE *out = _tfopen(((Files*) args)->pTarget,_T("wb")); if(in == NULL) { _tprintf(_T("in == NULL\n")); return (void*)1;} if(out == NULL) { _tprintf(_T("out == NULL\n")); return (void*)2;} // static const UINT SIZE=16384; // BYTE buffer[SIZE]; UINT SIZE = 16777216; // 16M BYTE* buffer=NULL; buffer = (BYTE*)malloc(SIZE); if(buffer==NULL) { _tprintf(_T("buffer == NULL\n")); return (void*)3; } // for(;;) while(!feof(in)) { DWORD Len = fread(buffer, 1, SIZE, in); // if(Len==0) { break; } fwrite(buffer, 1, Len, out); } fclose(in); fclose(out); free(buffer); return SUCCESS; } ----- Вот это попробуй для линкера: -D_REENTRANT У меня в проекте строка параметров линкера выглядит так: -Wall -pipe -D_REENTRANT -s -lpthread |
Сообщ.
#15
,
|
|
|
Цитата Sherman @ Вопрос был не в том, чтобы ты ответил, а в том, чтобы задумался, как должно было бы быть, и понял, почему стандартная библиотека должна знать о том, что она работает в многопоточном окружении и была реализована соответственно.Я совсем не давно начал заниматься программированием и затрудняюсь вам ответить. Цитата JoeUser @ Дык уже. Стандартная библиотека немногопоточная. Все признаки. Просьба к тебе, как к гуру Си и иже с ним... Разберись и проясни ситуацию. Тебе это обернется более "меньшей кровью" ввиду большего опыта. Добавлено Цитата Sherman @ В третий раз: почему не ferror()? if(Len==0) break; |
Сообщ.
#16
,
|
|
|
Цитата Qraizer @ В третий раз: почему не ferror()? Сделал так: int err = 0; int err2 = 0; do { DWORD Len = fread(buffer, sizeof(BYTE), buf, in); fwrite(buffer, sizeof(BYTE), Len, out); err = ferror (in); err2 = ferror (out); if (err != 0) printf("R: %s\r\n", strerror(errno)); if (err2 != 0) printf("W: %s\r\n", strerror(errno)); }while(!feof(in)); Выдаёт "W: Bad file descriptor" |
Сообщ.
#17
,
|
|
|
Цитата Qraizer @ Добавлено Цитата Sherman @ В третий раз: почему не ferror()? if(Len==0) break; Это очень просто. Если файл завершился или произошла ошибка - выход из цикла чтения. Поскольку запись в этом случае бессмысленна. А вот потом можно производить тест на ошибки. Поэтому это неверно: Добавлено Цитата Sherman @ Цитата Qraizer @ В третий раз: почему не ferror()? Сделал так: int err = 0; int err2 = 0; do { DWORD Len = fread(buffer, sizeof(BYTE), buf, in); fwrite(buffer, sizeof(BYTE), Len, out); err = ferror (in); err2 = ferror (out); if (err != 0) printf("R: %s\r\n", strerror(errno)); if (err2 != 0) printf("W: %s\r\n", strerror(errno)); }while(!feof(in)); Выдаёт "W: Bad file descriptor" Если Len равно 0 всё равно надо выходить из цикла. И потом разбираться с ошибками. |
Сообщ.
#18
,
|
|
|
Как можно реализовать похожую процедуру копирования файла на WIN API?
|
Сообщ.
#19
,
|
|
|
Цитата Sherman @ А зачем? Очередной раз говорю: проверь, что ты используешь многопоточную стандартную библиотеку. Тот факт, что код работает (хотя и написан не без изъянов) у всех, кроме тебя, ни на что не намекает? Совершенно неважно, каков у тебя движок потоков, хоть Плюсовый, хоть никсовый, хоть виндовый; если ты во многопоточном окружении используешь однопоточную CRT, она будет глючить, бажить и падать. Выхода два: первый уже озвучен, второй – вообще отказаться от любых стандартных функций языка. Как можно реализовать похожую процедуру копирования файла на WIN API? |
Сообщ.
#20
,
|
|
|
Цитата Qraizer @ Очередной раз говорю: проверь, что ты используешь многопоточную стандартную библиотеку. Да,но как это проверить? |
Сообщ.
#21
,
|
|
|
Цитата Sherman @ Вот с этим не помогу. Нужен спец по МинГВе. Да,но как это проверить? |
Сообщ.
#22
,
|
|
|
Цитата Qraizer @ Вот с этим не помогу. Нужен спец по МинГВе. Понял.В любом случае,спасибо за помощь. |
Сообщ.
#23
,
|
|
|
Цитата Sherman @ Цитата Qraizer @ Очередной раз говорю: проверь, что ты используешь многопоточную стандартную библиотеку. Да,но как это проверить? Так вот же: командная строка Используем поисковую систему, например www.ya.ru, пишем в строку "-D_REENTRANT". Смотрим, что получится. |