RE: pthreads (корректное использование)
Пользователи, просматривающие топик: none
|
Зашли как: Guest
|
Имя |
Сообщение |
<< Старые топики Новые топики >> |
|
|
RE: pthreads (корректное использование) - 2009-10-25 02:06:05.943333
|
|
|
Denaturat
Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
|
а почему не используешь Qt Threads? почему BuildServer и ReceiveFile в глобальном пространстве имён?
|
|
|
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 <QDialog>
#include <QFileDialog>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
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 &text, const char *member);
QComboBox *createComboBox(const QString &text = QString());
char *TakeName(QString file_name);
int AskOnRecv();
//void *BuildServer(void*); //???
//void *RecvFile(void*); //???
QFileDialog *OpenFileDialog;
};
#endif
Необходимо приделать к интерфейсу функции которые будут отвечать за прием данных, код функций уже есть (написан под консолью). Помимо этого необходимо чтобы за прослушивание входящих соединений отвечал 1 поток, за непосредственный прием n-потоков (возможно одновременно получать файлы с разных машин). С потоками работаю впервые поэтому не совсем понимаю где их нужно создавать и как задекларировать функции, которые будут выполняться этими потоками. Сейчас реализовано так: sender.cpp
#include <QtGui>
#include "sender.h"
static int sock_serv;
void *BuildServer(void*);
void *RecvFile(void*);
Sender::Sender(QWidget *parent)
: QDialog(parent)
{
sendButton = createButton(tr("&Send"), SLOT(sendFile()));
browseButton = createButton(tr("&Browse"), SLOT(browseFiles()));
...
mainLayout->addWidget(logLabel, 3, 0);
mainLayout->addWidget(logLineEdit, 3, 1);
mainLayout->addWidget(logButton, 3, 2);
mainLayout->addLayout(buttonsLayout, 5, 0, 1, 3);
setLayout(mainLayout);
setWindowTitle(tr("Send Files"));
resize(450, 150);
pthread_t tid;
pthread_create(&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 < 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 *)&addr_serv, sizeof(addr_serv)) < 0)
{
perror("bind");
exit(2);
}
listen(listener, 1); //режим ожидания запросов
while(1)
{
sock_serv = ::accept(listener, NULL, NULL);
if(sock_serv < 0)
{
perror("accept"); //ошибка создания нового сокета
exit(3);
}
else
{
pthread_t tid_;
pthread_create(&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*)&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, &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(); и вообще что-либо делать с элементами формы, а это необходимо еще для того чтобы получить директорию куда сохранить файл (сейчас это статичная строка). Прошу помочь привести эту кашу в более менее нормальный вид.
|
|
|
RE: pthreads (корректное использование) - 2009-10-25 03:00:03.150000
|
|
|
Lost_boy
Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
|
По заданию необходимо использовать pthreads. Если задекларировать функции в классе:
...
void *BuildServer(void*);
void *RecvFile(void*);
....
то появляются ошибки:
компиляция 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*); то я не могу их вызвать в потоке(
|
|
|
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() - функция-член объекта; передавай в поток указатель на этот объект и всё будет хорошо. про синхронизацию доступа только не забывай
|
|
|
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(&tid, NULL, BuildServer, obj);
Сама функция BuildServer:
void *BuildServer(void* obj)
{
...
pthread_t tid_;
Sender* obj_ = new Sender();
pthread_create(&tid_, NULL, ((Sender*) obj)->RecvFile, obj_);
}
функция RecvFile:
void *RecvFile(void* obj_)
{
int rez = ((Sender*) obj_)->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*)'
|
|
|
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)->RecvFile, obj_);
}
функция RecvFile:
void *RecvFile(void* obj_)
{
int rez = ((Sender*) obj_)->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*)'
указатель на метод сильно отличается от указателя на функцию (у них даже размер разный), так что компилятор говорит тебе чистую правду
|
|
|
RE: pthreads (корректное использование) - 2009-10-25 13:22:08.320000
|
|
|
Lost_boy
Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
|
Вынес объявления из класса. На счет BuildSever согласен, это по ошибке я автоматом написал передаваемый объект., поправил. Компильнул, пока работает сейчас буду тестить, спасибо что помогаешь)
|
|
|
RE: pthreads (корректное использование) - 2009-10-25 13:41:18.946666
|
|
|
Denaturat
Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
|
#include <iostream>
#include <pthread.h>
namespace Utils
{
void * callMethod(void *);
}
class Dummy
{
public:
void makeSomething()
{
std::cout << "In the dummy class object" << std::endl;
}
};
void * Utils::callMethod(void * obj)
{
reinterpret_cast<Dummy *>(obj)->makeSomething();
return 0;
}
int main()
{
Dummy obj;
pthread_t tid(0);
::pthread_create(&tid, NULL, &Utils::callMethod, reinterpret_cast<void*>(&obj));
::pthread_join(tid, NULL);
return 0;
}
на всякий случай вот тебе минимальный рабочий пример использования. ещё раз напоминаю, что функция callMethod (вернее, аналогичная твоя функция) должна позаботиться об уникальном доступе к объекту
|
|
|
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<void*>(&obj));
pthread_join(tid, NULL); //делал с ним и без
...
void *BuildServer(void* obj)
{
reinterpret_cast<Sender *>(obj)->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
*** Процесс прерван ***
Что я делаю не так?:@
|
|
|
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?
|
|
|
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<void*>(this));
если я правильно понимаю, чего ты хочешь (что не факт), то это всё что тебе нужно quote:
ORIGINAL: Lost_boy Что я делаю не так?:@ не думаешь прежде чем писать всякую пое~wересь. ну хоть отладчиком-то пройтись можно было? а pthread_join блокирует поток выполнения до завершения ожидаемого потока - в моём примере без него было не обойтись, а в твоём коде он, в общем-то, нафиг не нужен. документацию к POSIX Threads читать пробовал?
|
|
|
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 это я понимаю, поэтому и не использовал его, впримере написал на всякий случай (вдруг я что то не понял, но оказалось все верно)
|
|
|
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
|
|
|
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<void*>(this));
}
~Dummy()
{
::pthread_join(tid_, NULL);
}
void makeSomething()
{
std::cout << "In the dummy class object" << std::endl;
}
private:
pthread_t tid_;
};
void * Utils::callMethod(void * obj)
{
reinterpret_cast<Dummy *>(obj)->makeSomething();
return 0;
}
вопросы?
|
|
|
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 не решает проблему?
|
|
|
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())); Возможно так же описать сигнал создания формы и наисать для него слот в котором я буду создавать поток или это бред?
|
|
|
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
|
|
|
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 и прекрати сначала думать, а потом делать. обратный порядок куда как продуктивней
|
|
|
RE: pthreads (корректное использование) - 2009-10-25 23:57:50.770000
|
|
|
Lost_boy
Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
|
ушел исправлять пробелы в Qt и потоках) ночной секаз кам тру)
|
|
|
RE: pthreads (корректное использование) - 2009-10-28 15:35:43.410000
|
|
|
Lost_boy
Сообщений: 327
Оценки: 0
Присоединился: 2009-03-25 11:07:27.910000
|
Denaturat спасибо за помощь и пинки)) Все сделал, все работает отправка в отдельном треде, прослушка в отдельном треде, а проблему с вызовом ГУИ функции решил достаточно просто, в конструктор класса запихнул таймер, который проверяет глобальную переменную, а она меняет значение если на вход пришел файл, таким образом все запускается из ГУИ и генерируется отдельный тред для приема файла.
|
|
|
RE: pthreads (корректное использование) - 2009-10-28 18:29:13.323333
|
|
|
Denaturat
Сообщений: 1741
Оценки: 453
Присоединился: 2008-10-27 20:50:06.380000
|
quote:
ORIGINAL: Lost_boy в конструктор класса запихнул таймер, который проверяет глобальную переменную это, мягко говоря, плохое решение
|
|
|
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)->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); }
|
|
|
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); и чем тебе свободная функция не угодила?
|
|
|
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); з.ы. можно уже не флудить по теме, т.к. пока я не буду переделывать принцип работы.
|
|
|
|
|