Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: toggetit от Сентябрь 29, 2010, 16:30



Название: Многопоточное копирование файлов (Linux)
Отправлено: toggetit от Сентябрь 29, 2010, 16:30
Добрый день, форумчане!
Я тут начал изучать потоки и натнулся на такую задачку - создать программу, которая копирует структуру исходной директории в целевую, при этом для копирования файла создаётся отдельный поток и для директорий (проход/копирование) тоже отдельный. Начал писать такую программу, используя стандартные потоки линукс (phtread.h), ибо пока хочется изучить как оно работает, так сказать, нативно. Для навигации решил использовать Qt (QDir, QFileInfo etc.). При проходе/создании структуры папок вроде всё работает и очень хорошо (при этом прирост производительности в 15 раз по сравнению с однопоточным приложением), а вот как начинаю копировать файлы - облом! Сегфол! При этом если дожидаться завершения потока (pthread_join), то всё работает отлично. Уважаемые гуру - подскажите, что я не учёл.
Код: (не пугайтесь, я просто учусь  ;D)

Код:
void* fileCopyThread(void* arg)
{
    QPair<QString, QString>* p = (QPair<QString, QString>*)arg;
    QFile::copy(p->first, p->second);
    return NULL;
}

void* dirThread(void* arg)
{
        QPair<QDir, QDir>* temp = (QPair<QDir, QDir>*)arg;


        QFileInfoList list = (*temp).first.entryInfoList();
        for(QFileInfoList::iterator tmp = list.begin();tmp<list.end();tmp++)
        {
                if(((*tmp).fileName() == ".") || ((*tmp).fileName() == "..") || ((*tmp).isSymLink()))continue;

                if((*tmp).isFile() && (*tmp).exists())
                {

                        //Тут файловый поток
                        QPair<QString, QString> f;
                        f.first = (*tmp).absoluteFilePath();
                        f.second = (*temp).second.absolutePath() + "/" + (*tmp).fileName();
                        pthread_t tttt;

                        pthread_create(&tttt, NULL, fileCopyThread, (void*)(&f));
                        pthread_join(tttt, NULL);   //Если ждать - всё ок!
                        //fileCopyThread((void*)(&f));   //Это для проверки в "однопоточном" режиме
                        continue;
                }
                if((*tmp).isDir())
                {
                        //Вот тут рекурсию
                        (*temp).second.mkdir((*tmp).fileName());
                        QPair<QDir, QDir> t;
                        t.first = QDir((*tmp).filePath());
                        t.second = QDir((*temp).second.path() + "/" + (*tmp).fileName());
                        pthread_t tmpt;
                        pthread_create(&tmpt, NULL, dirThread, (void*)(&t));
                        pthread_join(tmpt, NULL);   //Ждём

                        //dirThread((void*)&t);  //Однопоток
                        continue;
                }

        }
        return NULL;
}



int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF8"));

    if(argc<2)
    {
            qDebug()<<"Беда с аргументами";
            return 0;
    }
    QPair<QDir, QDir> p;
    p.first = QDir(argv[1]);
    p.second = QDir(argv[2]);
    pthread_t thr1;
    pthread_create(&thr1, NULL, dirThread, (void*)(&p));
    pthread_join(thr1, NULL);

    return 0;
}


Название: Re: Многопоточное копирование файлов (Linux)
Отправлено: Rcus от Сентябрь 29, 2010, 16:48
QPair<QString, QString> создается на стеке одного потока, когда другой поток пытается оттуда читать то получает совсем не то что ожидал.


Название: Re: Многопоточное копирование файлов (Linux)
Отправлено: toggetit от Сентябрь 29, 2010, 18:02
То бишь выделять память в куче?


Название: Re: Многопоточное копирование файлов (Linux)
Отправлено: Igors от Сентябрь 30, 2010, 17:59
То бишь выделять память в куче?
Да, и поручить удаление запускаемой нитке.

А также:

- не гуд если нитки запускаются когда хотят, что ведет к неконтролируемому размножению. Больше ниток не значит быстрее

- программировать легко и приятно но только если не нужно отслеживать ошибки и обеспечивать возможность прервать операцию  :)

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

Ну на первое время этого хватит  :)


Название: Re: Многопоточное копирование файлов (Linux)
Отправлено: toggetit от Октябрь 01, 2010, 09:56
Усё. Память выделяю в куче. Нитка освобождает по завершению. Результат тот же - завершается прога на копировании файла. Предполагаю, что QFile::copy не thread-safe. Хотя вероятно, что я не прав

По поводу бесконтрольного размножения потоков - в задаче указано, что по одному потоку на каждый файл и папку, что собственно я и делаю. Или может я чего-то недопонял?


Название: Re: Многопоточное копирование файлов (Linux)
Отправлено: kuzulis от Октябрь 01, 2010, 10:37
Лучше скорее всего будет сделать сразу всего два потока, а не по каждому на файл и директорию!

Один будет проходить по дереву и "выискивать" нужные поддиректории директории/файлы и добавлять их по ходу прохода в очередь на обработку второму потоку. И по завершении прохода поток должен завершиться.

Второй будет собственно копировать то что у него висит в очередии и по опустошению очереди он должен завершиться..

Как то так.

Хотя, поищитут по форуму или спроси у Пантера http://www.prog.org.ru/index.php?action=profile;u=4067 т.к. он реализовывал нечто похожее в своем файловом менеджере http://gitorious.org/panthercommander/mainline/trees/master

См у него реализацию:
Цитировать
qfileoperationsthread.h
qfileoperationsthread.cpp


Название: Re: Многопоточное копирование файлов (Linux)
Отправлено: Igors от Октябрь 01, 2010, 13:09
По поводу бесконтрольного размножения потоков - в задаче указано, что по одному потоку на каждый файл и папку, что собственно я и делаю. Или может я чего-то недопонял?
Того что все начинается с разумной постановки задачи  :)

Лучше скорее всего будет сделать сразу всего два потока, а не по каждому на файл и директорию!
Это перебор в др. сторону - а если 4 ядра или 8, а мы используем всего 2? В данном случае очередь строится отлично и можно использовать оптимальное число ниток (= число ядер + 1). Лучше сразу избавиться от очевидного но неверного "1 нитка занимается 1 файлом"


Название: Re: Многопоточное копирование файлов (Linux)
Отправлено: toggetit от Октябрь 05, 2010, 09:34
Лучше скорее всего будет сделать сразу всего два потока, а не по каждому на файл и директорию!

Один будет проходить по дереву и "выискивать" нужные поддиректории директории/файлы и добавлять их по ходу прохода в очередь на обработку второму потоку. И по завершении прохода поток должен завершиться.

Второй будет собственно копировать то что у него висит в очередии и по опустошению очереди он должен завершиться..

Да, согласен, так даже интересней. Тут видится возможность потренироваться в использовании мьютексов
Спасибо


Название: Re: Многопоточное копирование файлов (Linux)
Отправлено: Авварон от Октябрь 05, 2010, 13:14
В qt solutions есть класс.


Название: Re: Многопоточное копирование файлов (Linux)
Отправлено: toggetit от Октябрь 05, 2010, 15:04
В qt solutions есть класс.

Будьте добры, скажите поподробней.


Название: Re: Многопоточное копирование файлов (Linux)
Отправлено: Авварон от Октябрь 05, 2010, 17:35
http://qt.nokia.com/products/appdev/add-on-products/catalog/4/Widgets/qtcopydialog/
http://doc.qt.nokia.com/solutions/4/qtcopydialog/qtcopydialog.html