Добро пожаловать! Это — архивная версия форумов на «Хакер.Ru». Она работает в режиме read-only.
 

RE: pthreads (корректное использование)

Пользователи, просматривающие топик: none

Зашли как: Guest
Все форумы >> [Компилируемые языки] >> RE: pthreads (корректное использование)
Имя
Сообщение << Старые топики   Новые топики >>
RE: pthreads (корректное использование) - 2009-10-25 02:06:05.943333   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
а почему не используешь Qt Threads? почему BuildServer и ReceiveFile в глобальном пространстве имён?
Post #: 1
pthreads (корректное использование) - 2009-10-25 02:58:22.963333   
Lost_boy

Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
Суть такая создается приложение которое будет передавать и получать файлы(пользователь может дать добро или отказать в получении файла), используются сокеты, потоки.
Есть класс:
sender.h
#ifndef SENDER_H #define SENDER_H #include &lt;QDialog&gt; #include &lt;QFileDialog&gt; #include &lt;stdlib.h&gt; #include &lt;stdio.h&gt; #include &lt;fcntl.h&gt; #include &lt;unistd.h&gt; #include &lt;sys/stat.h&gt; #include &lt;sys/types.h&gt; #include &lt;sys/socket.h&gt; #include &lt;netinet/in.h&gt; #include &lt;arpa/inet.h&gt; #include &lt;string.h&gt; #include &lt;pthread.h&gt; class QPushButton; class QLabel; class QString; class QFileDialog; class QComboBox; class QLineEdit; class Sender : public QDialog { Q_OBJECT public: Sender(QWidget *parent = 0); private slots: void sendFile(); void browseFiles(); void browseSave(); void browseLog(); void clear(); private: QPushButton *sendButton; QPushButton *browseButton; QPushButton *saveButton; QPushButton *logButton; QPushButton *clearButton; QComboBox *ipComboBox; QComboBox *fileComboBox; QLabel *ipLabel; QLabel *fileLabel; QLabel *saveLabel; QLabel *logLabel; QLineEdit *saveLineEdit; QLineEdit *logLineEdit; QPushButton *createButton(const QString &amp;text, const char *member); QComboBox *createComboBox(const QString &amp;text = QString()); char *TakeName(QString file_name); int AskOnRecv(); //void *BuildServer(void*); //??? //void *RecvFile(void*); //??? QFileDialog *OpenFileDialog; }; #endif Необходимо приделать к интерфейсу функции которые будут отвечать за прием данных, код функций уже есть (написан под консолью). Помимо этого необходимо чтобы за прослушивание входящих соединений отвечал 1 поток, за непосредственный прием n-потоков (возможно одновременно получать файлы с разных машин).

С потоками работаю впервые поэтому не совсем понимаю где их нужно создавать и как задекларировать функции, которые будут выполняться этими потоками.

Сейчас реализовано так:
sender.cpp
#include &lt;QtGui&gt; #include "sender.h" static int sock_serv; void *BuildServer(void*); void *RecvFile(void*); Sender::Sender(QWidget *parent) : QDialog(parent) { sendButton = createButton(tr("&amp;Send"), SLOT(sendFile())); browseButton = createButton(tr("&amp;Browse"), SLOT(browseFiles())); ... mainLayout-&gt;addWidget(logLabel, 3, 0); mainLayout-&gt;addWidget(logLineEdit, 3, 1); mainLayout-&gt;addWidget(logButton, 3, 2); mainLayout-&gt;addLayout(buttonsLayout, 5, 0, 1, 3); setLayout(mainLayout); setWindowTitle(tr("Send Files")); resize(450, 150); pthread_t tid; pthread_create(&amp;tid, NULL, BuildServer, NULL); } ... int Sender::AskOnRecv() { int answer = QMessageBox::question(this, tr("Recived file"), tr("You got a file. Take file?"), QMessageBox::Yes | QMessageBox::No); if (answer == 0x00004000) //была нажата кнопка Yes return 1; else return 0; } void *BuildServer(void*) { int listener; struct sockaddr_in addr_serv; listener = socket(AF_INET, SOCK_STREAM, 0); if(listener &lt; 0) { perror("socket"); //ошибка создания сокета exit(1); } addr_serv.sin_family = AF_INET; addr_serv.sin_port = htons(1100); addr_serv.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(listener, (struct sockaddr *)&amp;addr_serv, sizeof(addr_serv)) &lt; 0) { perror("bind"); exit(2); } listen(listener, 1); //режим ожидания запросов while(1) { sock_serv = ::accept(listener, NULL, NULL); if(sock_serv &lt; 0) { perror("accept"); //ошибка создания нового сокета exit(3); } else { pthread_t tid_; pthread_create(&amp;tid_, NULL, RecvFile, NULL); } } } void *RecvFile(void*) { //int rez = Sender::AskOnRecv(); ////необходимо добавить! //if (rez == 1) //{ while(1) { char buffer[200]; //буфер для получения файла char file_name[100]; //буфер имени принимаемого файла int bytes_read; //число получ. байтов struct stat client_st; bytes_read = recv(sock_serv, (char*)&amp;client_st, sizeof(client_st),0); unsigned long client_size = client_st.st_size; //контрольный размер файла char path[100] = "/home/lostboy/download/"; //шаблон директории длясохр. bytes_read = recv(sock_serv, file_name, 100, 0); //получаем имя файла strcat(path, file_name); //копируем полученое имя,получ. адрес для сохр. int deskriptor = open(path, O_CREAT | O_WRONLY, S_IWUSR); //создаем файл do { bytes_read = recv(sock_serv, buffer, 200, 0); if (bytes_read == 200) ::write(deskriptor, buffer, sizeof(buffer)); else ::write(deskriptor, buffer, bytes_read); } while (bytes_read != 0); //принимаем до тех пор пока есть что получать ::close(deskriptor); //закрываем файл struct stat server_st; stat (path, &amp;server_st); unsigned long server_size = server_st.st_size; if (client_size == server_size) { mode_t st_mode = client_st.st_mode; chmod (path, st_mode); printf("File is completely!\n"); break; } else { printf("Error.The file is not completely!\n"); break; } } //} //условие на принятие файла ::close(sock_serv); //закрываем сокет } Код рабочий, приложение получает файлы, но не спрашивая пользователя, в общем это не дело..
Идея этого кода такова: при создании формы создается поток в котором постоянно прослушивается канал, когда есть входной файл, создается новый поток для его приема, в этот момент пользователь должен увидеть сообщение о том, что он может принять файл (ф-я AskOnRecv();) если пользователь соглашается, то начинается прием файла, иначе нет.
Проблемы: некорректное объявление функций (нужно оформить в классе) и как следствие - невозможно вызвать ф-ю AskOnRecv(); и вообще что-либо делать с элементами формы, а это необходимо еще для того чтобы получить директорию куда сохранить файл (сейчас это статичная строка).
Прошу помочь привести эту кашу в более менее нормальный вид.
Post #: 2
RE: pthreads (корректное использование) - 2009-10-25 03:00:03.150000   
Lost_boy

Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
По заданию необходимо использовать pthreads.
Если задекларировать функции в классе:
... void *BuildServer(void*);&nbsp;&nbsp;&nbsp; &nbsp; void *RecvFile(void*);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; .... то появляются ошибки:
компиляция sender.cpp (g++) sender.cpp: In constructor 'Sender::Sender(QWidget*)': sender.cpp:81: error: argument of type 'void* (Sender::)(void*)' does not match 'void* (*)(void*)' sender.cpp: In member function 'void Sender::sendFile()': sender.cpp:142: warning: comparison between signed and unsigned integer expressions sender.cpp:223: warning: comparison between signed and unsigned integer expressions sender.cpp: In member function 'char* Sender::TakeName(QString)': sender.cpp:303: warning: comparison between signed and unsigned integer expressions sender.cpp: In function 'void* BuildServer(void*)': sender.cpp:362: error: 'RecvFile' was not declared in this scope если же описывать эти функции в sender.cpp вот так:
void *Sender::BuildServer(void*); то я не могу их вызвать в потоке(
Post #: 3
RE: pthreads (корректное использование) - 2009-10-25 05:10:20.160000   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
quote:

ORIGINAL: Lost_boy

Проблемы: некорректное объявление функций (нужно оформить в классе) и как следствие - невозможно вызвать ф-ю AskOnRecv()


AckOnRecv() - функция-член объекта; передавай в поток указатель на этот объект и всё будет хорошо. про синхронизацию доступа только не забывай
Post #: 4
RE: pthreads (корректное использование) - 2009-10-25 12:33:35.010000   
Lost_boy

Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
Делаю так:
в классе(sender.h), в привате:
void *BuildServer(void* obj);
void *RecvFile(void* obj_);

в конструкторе (sender.cpp):
pthread_t tid; Sender* obj = new Sender(); pthread_create(&amp;tid, NULL, BuildServer, obj); Сама функция BuildServer:
void *BuildServer(void* obj) { ... pthread_t tid_; Sender* obj_ = new Sender(); pthread_create(&amp;tid_, NULL, ((Sender*) obj)-&gt;RecvFile, obj_); } функция RecvFile:
void *RecvFile(void* obj_) { int rez = ((Sender*) obj_)-&gt;AskOnRecv(); ... } При компиляции получаю:
компиляция sender.cpp (g++) sender.cpp: In constructor 'Sender::Sender(QWidget*)': sender.cpp:82: error: argument of type 'void* (Sender::)(void*)' does not match 'void* (*)(void*)' sender.cpp: In function 'void* BuildServer(void*)': sender.cpp:366: error: argument of type 'void* (Sender::)(void*)' does not match 'void* (*)(void*)'
Post #: 5
RE: pthreads (корректное использование) - 2009-10-25 13:10:37.296666   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
quote:

ORIGINAL: Lost_boy

в классе(sender.h), в привате:
void *BuildServer(void* obj);
void *RecvFile(void* obj_);


зачем? вынеси их объявление из класса, тебе ведь вполне достаточно, что они работают с объектом твоего типа

quote:

ORIGINAL: Lost_boy
void *BuildServer(void* obj) { ... pthread_t tid_; Sender* obj_ = new Sender(); pthread_create(&tid_, NULL, ((Sender*) obj)-&gt;RecvFile, obj_); } функция RecvFile:
void *RecvFile(void* obj_) { int rez = ((Sender*) obj_)-&gt;AskOnRecv(); ... }


RecvFile более-менее соответствует логике программы, а вот BuildSever у тебя делает какую-то чушь. затем тебе туда передавть объект, если ты там его создаёшь?

quote:

ORIGINAL: Lost_boy

При компиляции получаю:
компиляция sender.cpp (g++) sender.cpp: In constructor 'Sender::Sender(QWidget*)': sender.cpp:82: error: [color=#FF0000]argument of type 'void* (Sender::)(void*)' does not match 'void* (*)(void*)'[/color] sender.cpp: In function 'void* BuildServer(void*)': sender.cpp:366: error: argument of type 'void* (Sender::)(void*)' does not match 'void* (*)(void*)'


указатель на метод сильно отличается от указателя на функцию (у них даже размер разный), так что компилятор говорит тебе чистую правду
Post #: 6
RE: pthreads (корректное использование) - 2009-10-25 13:22:08.320000   
Lost_boy

Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
Вынес объявления из класса.
На счет BuildSever согласен, это по ошибке я автоматом написал передаваемый объект., поправил.
Компильнул, пока работает сейчас буду тестить, спасибо что помогаешь)

Post #: 7
RE: pthreads (корректное использование) - 2009-10-25 13:41:18.946666   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
#include &lt;iostream&gt; #include &lt;pthread.h&gt; namespace Utils { void * callMethod(void *); } class Dummy { public: void makeSomething() { std::cout &lt;&lt; "In the dummy class object" &lt;&lt; std::endl; } }; void * Utils::callMethod(void * obj) { reinterpret_cast&lt;Dummy *&gt;(obj)-&gt;makeSomething(); return 0; } int main() { Dummy obj; pthread_t tid(0); ::pthread_create(&tid, NULL, &Utils::callMethod, reinterpret_cast&lt;void*&gt;(&obj)); ::pthread_join(tid, NULL); return 0; }
на всякий случай вот тебе минимальный рабочий пример использования. ещё раз напоминаю, что функция callMethod (вернее, аналогичная твоя функция) должна позаботиться об уникальном доступе к объекту
Post #: 8
RE: pthreads (корректное использование) - 2009-10-25 20:20:05.540000   
Lost_boy

Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
Решил не забегать вперед и сначала сделать 1 серверный поток, который будет запускаться в конструкторе, в нем будет и прослушивание канала иприем файла непосредственно. Объединил 2 функции BuildSever и RecvFile в одну, если не передаю в тред параметров для функции, все работает отлично. Теперь передаю параметр obj и все рушится, делал как раньше, потом на основе твоего примера:
void *BuildServer(void*); ... //в конструкторе: Sender obj; pthread_create(&tid, NULL, BuildServer, reinterpret_cast&lt;void*&gt;(&obj)); pthread_join(tid, NULL); //делал с ним и без ... void *BuildServer(void* obj) { reinterpret_cast&lt;Sender *&gt;(obj)-&gt;AskOnRecv(); } этот код не вызывает ошибок при компиляции, однако когда я запускаю приложение:
QThread::start: Thread creation error: QThread::start: Thread creation error: QThread::start: Thread creation error: QThread::start: Thread creation error: .... QThread::start: Thread creation error: QThread::start: Thread creation error: QIconvCodec::convertToUnicode: using ASCII for conversion, iconv_open failed QIconvCodec::convertFromUnicode: using ASCII for conversion, iconv_open failed terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc *** Процесс прерван *** Что я делаю не так?:@
Post #: 9
RE: pthreads (корректное использование) - 2009-10-25 22:54:18.463333   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
quote:

ORIGINAL: Lost_boy

//в конструкторе:
Sender obj;


погоди-ка. ты в конструкторе класса Sender создаёшь объект типа Sender?
Post #: 10
RE: pthreads (корректное использование) - 2009-10-25 22:59:22.220000   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
quote:

ORIGINAL: Lost_boy

//в конструкторе: pthread_create(&tid, NULL, BuildServer, reinterpret_cast&lt;void*&gt;(this));


если я правильно понимаю, чего ты хочешь (что не факт), то это всё что тебе нужно

quote:

ORIGINAL: Lost_boy

Что я делаю не так?:@


не думаешь прежде чем писать всякую пое~wересь. ну хоть отладчиком-то пройтись можно было?

а pthread_join блокирует поток выполнения до завершения ожидаемого потока - в моём примере без него было не обойтись, а в твоём коде он, в общем-то, нафиг не нужен. документацию к POSIX Threads читать пробовал?
Post #: 11
RE: pthreads (корректное использование) - 2009-10-25 23:06:44.706666   
Lost_boy

Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
Да инфу читал, просто сейчас надо максимально быстро реализовать потоки, вот и выходит что досконально со всем не разобрался.
quote:

ORIGINAL: Denaturat
погоди-ка. ты в конструкторе класса Sender создаёшь объект типа Sender?

Да, я просто не знаю куда это запихнуть, в студии я бы повесил это на какое-нибудь событие типа Form create, а тут не знаю где вызвать создание потока (нужно чтоб он создавался при открытии приложения) поэтому сейчас все это в конструкторе.

про pthread_join это я понимаю, поэтому и не использовал его, впримере написал на всякий случай (вдруг я что то не понял, но оказалось все верно)

Post #: 12
RE: pthreads (корректное использование) - 2009-10-25 23:12:17.563333   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
quote:

ORIGINAL: Lost_boy

Да, я просто не знаю куда это запихнуть, в студии я бы повесил это на какое-нибудь событие типа Form create, а тут не знаю где вызвать создание потока (нужно чтоб он создавался при открытии приложения) поэтому сейчас все это в конструкторе.


ещё раз, медленно и спокойно. ты создаёшь объект типа Sender в конструкторе класса Sender? тебя ничего не настораживает?

и я же тебе написал там выше, как от этого избавиться. передавай в поток this
Post #: 13
RE: pthreads (корректное использование) - 2009-10-25 23:21:59.070000   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
namespace Utils { void * callMethod(void *); } class Dummy { public: Dummy() : tid_(0) { ::pthread_create(&tid_, NULL, &Utils::callMethod, reinterpret_cast&lt;void*&gt;(this)); } ~Dummy() { ::pthread_join(tid_, NULL); } void makeSomething() { std::cout &lt;&lt; "In the dummy class object" &lt;&lt; std::endl; } private: pthread_t tid_; }; void * Utils::callMethod(void * obj) { reinterpret_cast&lt;Dummy *&gt;(obj)-&gt;makeSomething(); return 0; }
вопросы?
Post #: 14
RE: pthreads (корректное использование) - 2009-10-25 23:22:24.190000   
Lost_boy

Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
Да конечно, это неправильно
Сделал через this, приложение запускается, на пару секунд, я вижу что отрабатывает функция AskOnRecv() из:
void *BuildServer(void* obj)
{
reinterpret_cast<Sender *>(obj)->AskOnRecv();
}
но после этого сразу закрывается с кучей ошибок вот первое:
QObject::setParent: Cannot set parent, new parent is in a different thread QObject::installEventFilter(): Cannot filter events for objects in a different thread. как это новый родитель в другом потоке? значит this не решает проблему?
Post #: 15
RE: pthreads (корректное использование) - 2009-10-25 23:40:41.953333   
Lost_boy

Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
В Qt обработка сыобытий идет через сигналы и слоты. Т.е. на сигнал нажатия на кнопку вызывается слот в котором описано что необходимо делать, к примеру:
sendButton = createButton(tr("&Send"), SLOT(sendFile()));
Возможно так же описать сигнал создания формы и наисать для него слот в котором я буду создавать поток или это бред?
Post #: 16
RE: pthreads (корректное использование) - 2009-10-25 23:48:06.900000   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
ты что, издеваешься? или пишешь приложение методом научного тыка - авось заработает?

отладчик, твою дивизию, отладчик в зубы - и вперёд, искать причины проблем; судя по тому, с какой лёгкостью ты ввёл приложение в вечную рекурсию, знакомиться с отладчиком тебе надо тесно и надолго

http://lists.trolltech.com/qt-interest/2008-08/thread00119-0.html
http://forum.lafox.net/index.php?showtopic=19248

последняя ошибка, которую я комментирую в подобном формате - Qt не умеет вызывать методы, относящиеся к GUI, из другого потока. решение - отправлять в поток, заведующий GUI, сигнал, и отрабатывать его с той стороны. открываешь документацию по сигналам/слотам и изучаешь как это сделать:

http://doc.trolltech.com/4.5/signalsandslots.html
http://doc.trolltech.com/4.5/threads.html#signals-and-slots-across-threads
Post #: 17
RE: pthreads (корректное использование) - 2009-10-25 23:57:37.933333   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
quote:

ORIGINAL: Lost_boy

Возможно так же описать сигнал создания формы и написать для него слот в котором я буду создавать поток


у тебя в голове жуткая каша из форм, потоков и сигналов. попробуй выкинуть весь свой код нафиг, сесть с документацией, и разобраться для начала, что к чему

архитектурно же у тебя будет: один GUI-поток, один поток на обработчик, принимающий соединения; и по потоку на обработчики соединений. все GUI-операции должны выполняться в GUI-потоке, инициировать из других потоков ты их можешь с помощью отложенных сигналов, описанных в документации к Qt

и прекрати сначала думать, а потом делать. обратный порядок куда как продуктивней
Post #: 18
RE: pthreads (корректное использование) - 2009-10-25 23:57:50.770000   
Lost_boy

Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
ушел исправлять пробелы в Qt и потоках)
ночной секаз кам тру)
Post #: 19
RE: pthreads (корректное использование) - 2009-10-28 15:35:43.410000   
Lost_boy

Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
Denaturat спасибо за помощь и пинки))
Все сделал, все работает отправка в отдельном треде, прослушка в отдельном треде, а проблему с вызовом ГУИ функции решил достаточно просто, в конструктор класса запихнул таймер, который проверяет глобальную переменную, а она меняет значение если на вход пришел файл, таким образом все запускается из ГУИ и генерируется отдельный тред для приема файла.
Post #: 20
RE: pthreads (корректное использование) - 2009-10-28 18:29:13.323333   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
quote:

ORIGINAL: Lost_boy

в конструктор класса запихнул таймер, который проверяет глобальную переменную


это, мягко говоря, плохое решение
Post #: 21
RE: pthreads (корректное использование) - 2009-10-30 01:20:11.676666   
lindev

Сообщений: 37
Оценки: 0
Присоединился: 2008-07-07 02:50:15.793333
привет всем.

quote:

void *BuildServer(void* obj) { … pthread_t tid_; Sender* obj_ = new Sender(); pthread_create(&tid_, NULL, ((Sender*) obj)-&gt;RecvFile, obj_); }


// попробуй этот класс.

class Sender {
public:
pthread_t id;
void RecvFile(void *data);
static void *RecvFileStatic(void *data);
};

// функцию как параметер передавать нужно как статик

int main() {

Sender *ptr = new Sender();
pthread_create(&ptr->id, 0, Sender::RecvFileStatic, ptr);

pthread_join(ptr->id);
return 0;
}

void Sender::RecvFile(void *data) {
printf("Hello, world!");
}

// здесь, каст на объект Sender и вызыв нормалной функции RecvFile

void *Sender::RecvFileStatic(void *data) {
Sender *ptr = (Sender *)data;
ptr->RecvFile(ptr);
pthread_exit(0);
}
Post #: 22
RE: pthreads (корректное использование) - 2009-10-30 02:07:27.123333   
Denaturat

Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
quote:

ORIGINAL: lindev

static void *RecvFileStatic(void *data);


и чем тебе свободная функция не угодила?
Post #: 23
RE: pthreads (корректное использование) - 2009-10-30 09:21:38.033333   
Lost_boy

Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
Все как бы замечательно) не считая одного НО:
quote:

void Sender::RecvFile(void *data) {
printf("Hello, world!");
}

ты вызываешь ф-ю из класса которая не работает с ГУИ
в моем же случае мне необходимо чтоб функция выполняла немного другой код:
int answer = QMessageBox::question(this, tr("Recived file"),
tr("You got a file. Take file?"),
QMessageBox::Yes | QMessageBox::No);
з.ы. можно уже не флудить по теме, т.к. пока я не буду переделывать принцип работы.
Post #: 24
Страниц:  [1]
Все форумы >> [Компилируемые языки] >> RE: pthreads (корректное использование)







Связаться:
Вопросы по сайту / xakep@glc.ru

Предупреждение: использование полученных знаний в противозаконных целях преследуется по закону.