Выделить структурную память для аргументов для нескольких потоков

Следующий код запускает несколько потоков, и если поток уже запущен с тем же идентификатором, он отменяет предыдущий поток. Я использую этот код, чтобы перезапустить таймер для света.

Моя проблема в том, как повторно использовать параметры потока дважды? Я должен выделить память для структуры "thread_data" перед повторным использованием и перед повторным созданием потока... Это можно сделать с помощью карты "thread_list"? Может быть, я задаю не тот вопрос... Однако я уверен, что правильно ответил

#include <iostream>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#include <map>

using namespace std;

pthread_mutex_t mutex_t;
map <string, pthread_t> thread_list;

struct thread_data
{
    char *num;
    char *type;
    int time;
};

static void cleanup(void *arg)
{
    pthread_mutex_lock(&mutex_t);
    cout << "Thread Cleaned" << endl;
    pthread_mutex_unlock(&mutex_t);
}

static void *thread(void *arg)
{
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);

    pthread_cleanup_push(&cleanup,NULL);

    thread_data* my_data = (thread_data*)(arg);
    int time = my_data->time;

    pthread_mutex_lock(&mutex_t);
    cout << "Thread start " << time << " " << my_data->num << endl << flush;
    pthread_mutex_unlock(&mutex_t);

    while(time)
    {
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
        pthread_testcancel();
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);

        sched_yield();

        pthread_mutex_lock(&mutex_t);
        sleep(1);
        time--;
        pthread_mutex_unlock(&mutex_t);
    }

    pthread_mutex_lock(&mutex_t);
    cout << "Thread End " << endl;
    pthread_mutex_unlock(&mutex_t);

    pthread_cleanup_pop(0);

    return NULL;
}

void interrupt(string num, int time = 0, string type = "")
{
    cout << "Interruptable " << num << " " << time << " " << type << endl;

    if (thread_list.find(num)->second)
    {
        cout << "Cancel " << num << endl;

        if (pthread_cancel(thread_list[num]) == 0)
        {
            pthread_detach(thread_list[num]);
            while (pthread_kill(thread_list[num], 0)==0)
                sched_yield();
        }

        thread_list.erase(num);
    }

    thread_data td;
    td.time = time;
    td.num = "Ok";

    pthread_t thread_id;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    pthread_mutex_init(&mutex_t, NULL);
    pthread_create (&thread_id, &attr, &(thread), (void *)&td);
    thread_list.insert(pair <string, pthread_t> (num, thread_id));
}

int main()
{
    cout << "Start main" << endl;

    interrupt("6335", 5, "");
    sleep(1);
    interrupt("6335", 2, "");
    sleep(6);

    cout << "End main" << endl;
    return 0;
}

person ysirob    schedule 10.03.2015    source источник
comment
Я предлагаю выделить данные потока в куче и освободить (или удалить: удалить) их в регистрируемой вами функции очистки потока. Отправка вещей из стека — это нехорошо. Кроме того, используйте C++11 std mutex:es и примитивы потоков вместо потоков posix, если вы хотите, чтобы их было легче переносить на ОС, отличные от Unix, такие как Windows. Третье улучшение: используйте RAII для примитивов потоков/мьютексов вместо ручной разблокировки и т. д. std::lock_guard.   -  person Erik Alapää    schedule 10.03.2015
comment
Да данные темы нужно удалить..... я   -  person ysirob    schedule 10.03.2015
comment
Нет, если он статический/глобальный или в стеке.   -  person Erik Alapää    schedule 10.03.2015
comment
Да, данные потока должны быть удалены в функции очистки, передавая данные потока функции pthread_cleanup_push, я думаю... Я пытался отменить поток с помощью примитивов потока С++ 11, я использовал собственную функцию дескриптора, чтобы отменить их, и мне это не удалось.   -  person ysirob    schedule 10.03.2015


Ответы (2)


Прежде всего, похоже, вы пытаетесь использовать структуру, не выделяя для нее памяти:

 thread_data td;
 td.time = time;
 td.num = "Ok";

Этот фрагмент кода может показаться мне ошибочным, потому что переменной td не назначена память (кроме стека), но он пытается присвоить значение. Таким образом, после создания потока и возврата функции память будет повреждена. По этой причине вы никогда не должны использовать локальную переменную для передачи данных в другую функцию. Обычно я ожидаю, что структуре будет выделена некоторая память кучи, например:

 thread_data td;
 td = malloc( sizeof( thread_data ) );
 if( td == NULL ) ... [error handling] ...
 td.time = time;
 td.num = "Ok";

Каждый раз, когда вам нужна эта структура, вы должны выделять для нее память. Обычно (за исключением специализированного высокопроизводительного приложения) не следует пытаться повторно использовать память.

Кроме того, обратите внимание, что дизайн ошибочен, потому что нет способа освободить память, выделенную для структуры td. На самом деле вы должны создать структуру td на более высоком уровне, например, в main(), а затем, когда функция interrupt() вернется, вы должны освободить используемую память. Например:

   main(){
     ....
     thread_data td;
     td = malloc( sizeof( thread_data ) );
     if( td == NULL ) ... [error handling] ...
     td.time = time;
     td.num = "Ok";
     interrupt( td );
     ...
     [do next interrupt]
     ...
     [after worker thread is complete]
     free( td );

Или что-то вдоль этих линий.

person Tyler Durden    schedule 10.03.2015
comment
Структура td находится в стеке, поэтому у нее есть память. Но отправлять вещи из стека — не такая уж хорошая идея. - person Erik Alapää; 10.03.2015
comment
@ErikAlapää Поскольку структура передается другой функции, это может привести к тому, что память будет использоваться после возврата функции, что приведет к ошибке. Я отредактировал свой ответ, чтобы более подробно рассказать об этом. - person Tyler Durden; 10.03.2015
comment
Точно моя точка зрения, если вы читаете мои другие комментарии. Я предложил выделить в куче и очистить в thread's cleanup fcn. - person Erik Alapää; 10.03.2015
comment
Я думаю, что данные потока должны быть очищены в отдельном потоке. Я не знаю продолжительность и количество необходимых потоков. Мне нужно одновременно запустить switch_light (число, продолжительность) и отменить функцию, когда она была запущена с тем же числом в параметре. - person ysirob; 10.03.2015
comment
@ksirob Правда, я просто говорил в общем. Теоретически, если данные используются в течение всего времени жизни потока, то их можно освободить только после того, как поток умрет. - person Tyler Durden; 10.03.2015
comment
@ksirob, если данные, используемые рабочим потоком, должны жить дольше, чем поток, просто malloc в основном потоке, а также free() в основном потоке после объединения всех рабочих потоков, непосредственно перед выходом из основного потока. - person Erik Alapää; 10.03.2015

Я установил «td» в структуре «thread_data». Не получается после долгого времени....

struct thread_data
{
    int duration;
    string num;
    string type;
} td;

Полный код:

#include <iostream>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#include <map>

using namespace std;

pthread_mutex_t mutex_t;
map <string, pthread_t> thread_list;

struct thread_data
{
    int duration;
    string num;
    string type;
} td;

static void cleanup(void *arg)
{
    pthread_mutex_lock(&mutex_t);
    cout << "Thread Cleaned" << endl;
    pthread_mutex_unlock(&mutex_t);
}

static void *thread(void *arg)
{
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);

    pthread_cleanup_push(&cleanup,NULL);

    thread_data* my_data = (thread_data*)(arg);
    int duration = my_data->duration;

    pthread_mutex_lock(&mutex_t);
    cout << "Thread start " << duration << " " << my_data->num << endl;
    pthread_mutex_unlock(&mutex_t);

    while(duration)
    {
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
        pthread_testcancel();
        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);

        sched_yield();

        pthread_mutex_lock(&mutex_t);
        sleep(1);
        duration--;
        pthread_mutex_unlock(&mutex_t);
    }

    pthread_mutex_lock(&mutex_t);
    cout << "Thread End " << endl;
    pthread_mutex_unlock(&mutex_t);

    pthread_cleanup_pop(0);

    return NULL;
}

void timer_launch(int duration, string num, string type)
{
    cout << "Interruptable " << num << " " << duration << " " << type << endl;

    if (thread_list.find(num)->second)
    {
        cout << "Cancel " << num << endl;

        if (pthread_cancel(thread_list[num]) == 0)
        {
            pthread_detach(thread_list[num]);
            while (pthread_kill(thread_list[num], 0)==0)
                sched_yield();
        }
        thread_list.erase(num);
    }

    td.duration = duration;
    td.num = num;
    td.type = type;

    pthread_t thread_id;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    pthread_mutex_init(&mutex_t, NULL);
    pthread_create (&thread_id, &attr, &(thread), (void *)&td);

    thread_list.insert(pair <string, pthread_t> (td.num, thread_id));
}

int main()
{
    cout << "Start main" << endl;

    timer_launch(5, "6335", "div");
    sleep(1);
    timer_launch(3, "6335", "div");
    sleep(6);

    cout << "End main" << endl;
    return 0;
}
person ysirob    schedule 10.03.2015
comment
Я знаю, что выделение структуры - это плохо, но я хочу иметь функцию, которую я могу отменить и запустить, не останавливая мою программу.... без присоединения к потокам.. - person ysirob; 02.06.2015