LINUX.ORG.RU

Управление потоками с использованием pthread

 ,


1

2

Всем привет, я использую QT Creator в качестве иде и вот исходники проекта:

про файл:

TEMPLATE = app
CONFIG += console
CONFIG -= qt
LIBS += -lpthread

SOURCES += main.cpp

мейн файл:

#include <iostream>
#include <pthread.h>

int d=0;

using namespace std;

void* task1(void* X)
{
    int OldState, OldType;

    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &OldState);
        pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &OldType);

    for(int Count=1; Count<100; Count++)
    {
        d++;
        cout << "В потоке А: " << Count << '\n';
    }
    pthread_testcancel();
}

int main()
{
    pthread_t Threads;
    void* Status;

    pthread_create(&(Threads), NULL, task1, NULL);

    pthread_cancel(Threads);

        pthread_join(Threads, &Status);
        if(Status==PTHREAD_CANCELED)
        {
            cout << "Поток " << " аннулирован" << endl;
        }
        else
        {
            cout << "Поток " << " продолжает выполнение" << endl;
        }
    cout << "d: " << d << endl;
    return 0;
}
проблема вот в чём: при вызове pthread_cancel(Threads); вывод программы такой: В потоке А: 1. И всё, больше ничего, если закомментить этот вызов то всё работает как и задумано(цикл производит 99 итераций), подскажите пожалуйста в чём тут может быть проблема.



Последнее исправление: cetjs2 (всего исправлений: 3)

Ответ на: комментарий от bumbaram

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

Onito
() автор топика
Ответ на: комментарий от avc_fan

task1 является отменяемой косвенно, через те функции, которые она вызывает, в том числе и неявно. Т.е. если сама libpthread в функции-прологе для нового потока вызывает отменяемые функции, то поток может даже не запуститься. Итог исполнения в данном случае зависит от планирования потоков.

Неправильное использование тут в том смысле, что ТС считает (считал?) эту функцию штатным механизмом «попросить» поток завершить своё исполнение, в то время как, по факту, это скорее экстренное завершение потока, которое следовало назвать «terminate».

xaizek ★★★★★
()
Ответ на: комментарий от bumbaram

и даже если так, мы убедились что cout'ы генерируют точки выхода, но почему тогда основной поток ломается? даже если все потоки вылетают при первой итерации он должен в конце вывести что потоки аннулированы и значение переменной, а ничего этого нет, или иногда напишет про какойто рандомный поток про 1 2 или 3 без всякой логики а переменную так вообще никогда не выводит

Onito
() автор топика
Ответ на: комментарий от Onito

Видимо, ломается не поток управления, а вывод в поток ввода-вывода из множества потоков исполнения. Стандартная библиотека не гарантирует правильную последовательность подобных операций, они будут перемежаться случайным образом и приводить к трудно-предсказуемым результатам. Тут надо хотя бы собирать строку для вывода целиком, а затем передавать её в cout, а по-нормальному так использовать pthread_mutex, как уже предлагали выше.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

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

Onito
() автор топика
Ответ на: комментарий от Onito

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

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

тоесть это работает так: объявляется переменная с спецификатором volatile и значение true и цикл в потоке работает по ней а когда нужно его завершить из основного потока её значение меняется на false и таким образом просто заканчивается цикл а значит и весь поток, но вообще это корректно? А то может например совпасть считывания переменной в потоке и изменений её в управляющем например

Onito
() автор топика
Ответ на: комментарий от Onito

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

xaizek ★★★★★
()
Ответ на: комментарий от Onito

Попробуйте заменить вывод в cout на вывод в файл, причем создать разные файлы для потоков. Возможно, это позволит точно установить, что виноват именно поломанный cout.

trex6 ★★★★★
()
Ответ на: комментарий от trex6

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

Onito
() автор топика
Ответ на: комментарий от Onito

Я подозреваю, что они точно так же создают точку для прерывания потока.

trex6 ★★★★★
()
Ответ на: комментарий от trex6

вот вывод отладчика при закомменченых cout'ах: [Thread debugging using libthread_db enabled] Using host libthread_db library «/lib/x86_64-linux-gnu/libthread_db.so.1». [New Thread 0x7ffff7096700 (LWP 3758)] [Thread 0x7ffff7096700 (LWP 3758) exited] [New Thread 0x7ffff6895700 (LWP 3759)] [Thread 0x7ffff6895700 (LWP 3759) exited] [New Thread 0x7ffff6094700 (LWP 3760)] [Thread 0x7ffff6094700 (LWP 3760) exited] Поток 0 продолжает выполнение Поток 1 продолжает выполнение Поток 2 продолжает выполнение d: 298 [Inferior 1 (process 3755) exited normally]

А вот вывод с рабочими cout'ами: [Thread debugging using libthread_db enabled] Using host libthread_db library «/lib/x86_64-linux-gnu/libthread_db.so.1». [New Thread 0x7ffff7096700 (LWP 3821)] В потоке А: 1 [New Thread 0x7ffff6895700 (LWP 3822)] В потоке А: 2 В потоке А: 3 В потоке А: 4 В потоке А: 5 В потоке А: 6 В потоке А: 7 В потоке А: 8 [New Thread 0x7ffff6094700 (LWP 3823)] В потоке А: 9 В потоке А: 10 В потоке А: 11 В потоке C: В потоке А: 12 В потоке А: 13 В потоке А: 14 В потоке А: 15 В потоке А: 16 В потоке А: 17 В потоке А: 18 В потоке А: 19 В потоке А: 20 В потоке А: 21 1[Thread 0x7ffff6895700 (LWP 3822) exited] [Thread 0x7ffff7096700 (LWP 3821) exited] [Thread 0x7ffff6094700 (LWP 3823) exited] [Inferior 1 (process 3818) exited normally]

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

Onito
() автор топика

QT Creator в качестве иде и вот исходники проекта:

Quick Time Creator в качестве IDE? Сильно.

andreyu ★★★★★
()
2 июня 2014 г.
Ответ на: комментарий от Onito

Все элементарно, для этого стоит почитать про pthreads.

Вы стартуете потоки и сразу же их прерываете, при этом не заботясь о синхронизации. Операции I/O всегда занимают приличное количество процессорного времени, поэтому при их добавлении шанс успешного завершения приложения у Вас резко падает. Но в любом случае причина не в этом, а в отсутствии синхронизации потоков. Зачем Вам вообще pthread_cancel? Если вы хотите, чтобы все потоки выполнили свою работу, используйте join и ждите их завершения. mutex в случае с pthread_cancel вам мало чем поможет, по крайней мере без извратов.

А вообще код слегка странный и не совсем понятно, что ж он в итоге делать-то должен. В общем либо нормальная синхронизация, либо выкинуть cancel вообще.

anonymous
()
Ответ на: комментарий от anonymous

Более подробно:

int main()
{
    pthread_t Threads; /* сюда только 1 дескриптор поместится, так что thread */
    void* Status;

    pthread_create(&(Threads), NULL, task1, NULL); /* создали поток, он (возможно) даже успел начать выполнение */

    pthread_cancel(Threads); /* убили поток, причем не важно, успел ли он что-то сделать */

        pthread_join(Threads, &Status); /* подождали, что поток реально умер. Зачем? Мы его строчкой выше убили */
        if(Status==PTHREAD_CANCELED) /* проверили, что это мы убили поток, а не он сам умер. Потому как после join он уже 100% мертв */
        {
            cout << "Поток " << " аннулирован" << endl; /* ага, убили мы */
        }
        else
        {
            cout << "Поток " << " продолжает выполнение" << endl; /* это как? после join он ну никак не может продолжать выполнение */
        }
    cout << "d: " << d << endl; /* посчитали, что же он успел до того, как получил по шапке от main */
    return 0;
}
anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.