C++: потоки (обращение к внешним переменным)

Модератор: Модераторы разделов

IMB
Сообщения: 2567
ОС: Debian

C++: потоки

Сообщение IMB »

Доброго дня!
Есть обёртка над pthread_create вида:

Код: Выделить всё

unsigned int startTh(pthread_t *thread, const pthread_attr_t *attr,
                    void *(*func)(void *), void *arg)
        {
            return pthread_create(thread, attr, func, arg);
        }

Фукция, которая должна стартовать в потоке, со следующим прототипом - void *start(void *arg).
При таком прототипе компиляция заканчивается ошибкой:

Код: Выделить всё

stream.cpp: In constructor ‘Stream::Stream()’:
stream.cpp:47: error: no matching function for call to ‘Stream::startTh(pthread_t*, pthread_attr_t*, <unresolved overloaded function type>, int)’
thread.hpp:12: note: candidates are: unsigned int Thread::startTh(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)

В случае, если я привожу прототип фукции к static void *start(void *arg), данной ошибке при компиляции не возникает.
Но в этом случае я не могу обратиться к необходимым мне переменным, ошибка приобретает вид:

Код: Выделить всё

stream.hpp:33: error: invalid use of member ‘Stream::imageWidth’ in static member function
stream.cpp:118: error: from this location

Описание класса:

Код: Выделить всё

class Stream : protected Thread {
    typedef struct source_buff_s {
        unsigned char *buff;
        unsigned int  size;
        char          *url;
        pthread_mutex_t mutex;
    } source_buff_t;
    bool is_meta;
    bool is_jpeg;
    unsigned int imageWidth;
    unsigned int imageHeight;
    source_buff_t m_jpeg;
    source_buff_t m_meta;
    pthread_t streamThread;
    pthread_attr_t attr;
    public:
        Stream();
        ~Stream(){}
    private:
        void *start(void *arg);

Передать необходимые аргументы в фукцию через структуру нельзя, так как к ним необходим доступ другой фукции этого класса из другого потока.
Вопрос возник потому, что во всех примерах по работе с потоками, которые я нашёл, не используется указатель static для фукций.
Вопрос - как необхомо вызывать фукцию в потоке что бы она имела доступ, и могла оперировать, внешними переменными?
Использую gcc 4.2.0, если это имеет значение.
Спасибо.
Спасибо сказали:
mrcashe
Сообщения: 18

Re: C++: потоки

Сообщение mrcashe »

IMB писал(а):
09.06.2010 17:22
Доброго дня!
Есть обёртка над pthread_create вида:

Код: Выделить всё

unsigned int startTh(pthread_t *thread, const pthread_attr_t *attr,
                    void *(*func)(void *), void *arg)
        {
            return pthread_create(thread, attr, func, arg);
        }

Фукция, которая должна стартовать в потоке, со следующим прототипом - void *start(void *arg).
При таком прототипе компиляция заканчивается ошибкой:

Код: Выделить всё

stream.cpp: In constructor ‘Stream::Stream()’:
stream.cpp:47: error: no matching function for call to ‘Stream::startTh(pthread_t*, pthread_attr_t*, <unresolved overloaded function type>, int)’
thread.hpp:12: note: candidates are: unsigned int Thread::startTh(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)

В случае, если я привожу прототип фукции к static void *start(void *arg), данной ошибке при компиляции не возникает.
Но в этом случае я не могу обратиться к необходимым мне переменным, ошибка приобретает вид:

Код: Выделить всё

stream.hpp:33: error: invalid use of member ‘Stream::imageWidth’ in static member function
stream.cpp:118: error: from this location

Описание класса:

Код: Выделить всё

class Stream : protected Thread {
    typedef struct source_buff_s {
        unsigned char *buff;
        unsigned int  size;
        char          *url;
        pthread_mutex_t mutex;
    } source_buff_t;
    bool is_meta;
    bool is_jpeg;
    unsigned int imageWidth;
    unsigned int imageHeight;
    source_buff_t m_jpeg;
    source_buff_t m_meta;
    pthread_t streamThread;
    pthread_attr_t attr;
    public:
        Stream();
        ~Stream(){}
    private:
        void *start(void *arg);

Передать необходимые аргументы в фукцию через структуру нельзя, так как к ним необходим доступ другой фукции этого класса из другого потока.
Вопрос возник потому, что во всех примерах по работе с потоками, которые я нашёл, не используется указатель static для фукций.
Вопрос - как необхомо вызывать фукцию в потоке что бы она имела доступ, и могла оперировать, внешними переменными?
Использую gcc 4.2.0, если это имеет значение.
Спасибо.


Дело в том, что функция start() - это член-функция класса, первым аргументом которой является неявно передаваемый указатель this, так что прототип её в корне отличается от поточной функции. Чтобы вызывать такую функцию, необходимо знать адрес экземпляра класса Stream, а это не предусмотрено интерфейсом функции pthread_create().
В таких случаях обычно делают функцию статической и в качестве аргумента передают ей адрес объекта:

Код: Выделить всё

class Stream : protected Thread {
    typedef struct source_buff_s {
        unsigned char *buff;
        unsigned int  size;
        char          *url;
        pthread_mutex_t mutex;
    } source_buff_t;
    bool is_meta;
    bool is_jpeg;
    unsigned int imageWidth;
    unsigned int imageHeight;
    source_buff_t m_jpeg;
    source_buff_t m_meta;
    pthread_t streamThread;
    pthread_attr_t attr;
    public:
        Stream();
        ~Stream(){}
    private:
        static void *start(Stream * stream);


И теперь делать так:

Код: Выделить всё

void * Stream::start(Stream * stream) {
    stream->imageWidth = 200;
    ...
    return 0;
}

Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: C++: потоки

Сообщение IMB »

mrcashe писал(а):
09.06.2010 18:56
И теперь делать так:

Код: Выделить всё

void * Stream::start(Stream * stream) {
    stream->imageWidth = 200;
    ...
    return 0;
}

Хорошо, это, как я понимаю, установка значения, а при считывание значения что необходимо учесть?
Спасибо сказали:
math
Сообщения: 290
Статус: Ъ участник
ОС: Artix (= Arch without systemd)

Re: C++: потоки

Сообщение math »

IMB писал(а):
09.06.2010 19:47
Хорошо, это, как я понимаю, установка значения, а при считывание значения что необходимо учесть?

Код: Выделить всё

void * Stream::start(Stream * stream) {
    int a = stream->imageWidth;
    ...
    return 0;
}
Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: C++: потоки

Сообщение IMB »

Хорошо, я так понимаю, что данный код должен работать.

Код: Выделить всё

 int B;
class Test {
 int A;
 int read() {cout << "A have value " << this->A << endl;}
 void write() {this->A = 100;}
};

А как считать/записать глобальную переменную, общую для всей программы?
Спасибо сказали:
math
Сообщения: 290
Статус: Ъ участник
ОС: Artix (= Arch without systemd)

Re: C++: потоки

Сообщение math »

IMB писал(а):
09.06.2010 20:52

Код: Выделить всё

int B;
class Test {
    int A;
    int read() {cout << "A have value " << this->A << endl;}
    void write() {this->A = 100;}
};

А как считать/записать глобальную переменную, общую для всей программы?

Вы вообще про что? Про Stream или нет? И код этот здесь при чём?
Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: C++: потоки

Сообщение IMB »

math писал(а):
09.06.2010 21:24
Вы вообще про что? Про Stream или нет? И код этот здесь при чём?

Нет, в данном случае я привёл абстрактный код для проверки того, что я правильно понял.
Спасибо сказали:
math
Сообщения: 290
Статус: Ъ участник
ОС: Artix (= Arch without systemd)

Re: C++: потоки

Сообщение math »

IMB писал(а):
09.06.2010 21:27
math писал(а):
09.06.2010 21:24
Вы вообще про что? Про Stream или нет? И код этот здесь при чём?

Нет, в данном случае я привёл абстрактный код для проверки того, что я правильно понял.

Вы лучше приведите конкретный код, а я (или кто-нибудь ещё) скажу почему он не работает (если он не работает) ил работает не так.
Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: C++: потоки

Сообщение IMB »

Привёл протопит фукции к рекомендованому виду - static void *start(Stream *s)
Ошибка при компиляции:

Код: Выделить всё

stream.cpp:47: error: invalid conversion from ‘void* (*)(Stream*)’ to ‘void* (*)(void*)’
stream.cpp:47: error:   initializing argument 3 of ‘unsigned int Thread::startTh(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)’
Спасибо сказали:
math
Сообщения: 290
Статус: Ъ участник
ОС: Artix (= Arch without systemd)

Re: C++: потоки

Сообщение math »

IMB писал(а):
10.06.2010 09:23
Привёл протопит фукции к рекомендованому виду - static void *start(Stream *s)
Ошибка при компиляции:

Код: Выделить всё

stream.cpp:47: error: invalid conversion from ‘void* (*)(Stream*)’ to ‘void* (*)(void*)’
stream.cpp:47: error:   initializing argument 3 of ‘unsigned int Thread::startTh(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)’

Есть два способа преодолеть это:
1) явно привести указатель на функцию при передаче его в pthread_start:

Код: Выделить всё

startTh(..., ..., reinterpret_cast<void* (*)(void*)>(start), ...)

2) сделать прототип

Код: Выделить всё

static void *start(void *vs);

а внутри делать так

Код: Выделить всё

void * Stream::start(void *vs) {
    Stream *s = reinterpret_cast<Stream *>(vs);
    ...
    return 0;
}
Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: C++: потоки

Сообщение IMB »

Пошёл по второму пути - приведением к типу внутри функции, компиляция прошла без ошибок. Но теперь работа заканчивается по Segmentation fault.
Последние строки вывода strace:

Код: Выделить всё

mmap2(NULL, 266240, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 00
mmap2(NULL, 266240, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 00
sched_get_priority_max(SCHED_FIFO)      = 99
mmap2(NULL, 8388608, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS,0
mprotect(0x40388000, 4096, PROT_NONE)   = 0
sched_get_priority_min(SCHED_FIFO)      = 1
sched_get_priority_max(SCHED_FIFO)      = 99
clone(child_stack=0x40b86fe8, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND1
sched_setscheduler(291, SCHED_FIFO, { 99 }) = 0
futex(0x40b87684, FUTEX_WAKE, 1)        = 1
+++ killed by SIGSEGV +++

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

Код: Выделить всё

#include <pthread.h>

template <typename T> class BaseThread
{
    protected:
        pthread_t thread;

        typedef void (T::*run_func_t)(void);

        typedef struct thread_arg_s
        {
            T *p_this;
            run_func_t run_func;
        } thread_arg_t;

        thread_arg_t thread_arg;

        static void *thread_func(void *arg)
        {
            thread_arg_t *thread_arg = (thread_arg_t *)arg;
            (thread_arg->p_this->*thread_arg->run_func)();
            pthread_exit(NULL);
        }
    public:
        BaseThread()
            :thread(0xFFFFFFFF)
            ,thread_arg()
        {
        }

        ~BaseThread()
        {
        }

        void start(T *p_this, run_func_t run_func)
        {
            thread_arg.p_this   = p_this;
            thread_arg.run_func = run_func;
            if(pthread_create(&thread, NULL, &thread_func, (void*)&thread_arg) != 0)
            {
            }
        }

        void wait()
        {
            if(0xFFFFFFFF != thread)
            {
                pthread_join(thread, NULL);
            }
        }

        void cancel()
        {
            if(0xFFFFFFFF != thread)
            {
                pthread_cancel(thread);
            }
        }
};

Код: Выделить всё

#include "basethread.h"

class Thread
{
    protected:
        bool stop_flag;

        BaseThread<Thread> *bthread;

        virtual void run (void) = 0;

    public:
        Thread()
            :stop_flag(false)
            ,bthread(new BaseThread<Thread>())
        {
        }

        virtual ~Thread()
        {
            delete(bthread);
        }

        void start  (void);
        void stop   (void);
        void wait   (void);
        void cancel (void);
};

Код: Выделить всё

#include "thread.h"

void Thread::start(void)
{
    bthread->start(this, &Thread::run);
}

void Thread::stop(void)
{
    stop_flag = true;
}

void Thread::wait(void)
{
    bthread->wait();
}

void Thread::cancel(void)
{
    bthread->cancel();
}

Спасибо сказали:
mrcashe
Сообщения: 18

Re: C++: потоки

Сообщение mrcashe »

Вот Вам рабочий пример:

Код: Выделить всё

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

const char * global_var = "This is global variable";

class Stream {
    private:

        pthread_t th_;

    public:

        Stream() {
            pthread_create(&th_, NULL, start, this);
        }

        static void * start(void * arg) {
            Stream * stream = reinterpret_cast<Stream *>(arg);
            std::cout << "Thread is running, global_var = \"" << global_var << "\"\n";
            pthread_cancel(stream->th_);
        }

        void join() {
            pthread_join(th_, NULL);
            std::cout << "After join()\n";
        }
};

int main(int argc, char * argv[]) {
    Stream s;
    s.join();
    return 0;
}


Указатель this передаётся в качестве аргумента потоковой функции в конструкторе класса. Дабы не приводить тип аргумента 3 функции pthread_create(), приводится тип указаиеля непосредственно в потоковой функции. Так изящнее получается.
Спасибо сказали:
Ramol
Сообщения: 44
ОС: Debian

Re: C++: потоки

Сообщение Ramol »

Можно кстати воспользоваться boost::bind если нет никаких ограничения по добавлению сторонних библиотек и не надо будет делать функции статиками
Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: C++: потоки

Сообщение IMB »

Да особо никаких ограничений нет, но всё это добро будет работать на встраиваемом устройстве и поэтому хочется обойтись минимальным включением стороних библиотек и, как следствие, объёмом кросс-компилирования.
Спасибо сказали:
Ramol
Сообщения: 44
ОС: Debian

Re: C++: потоки

Сообщение Ramol »

ну можно еще для класса перегрузить operator()() функции и в нем вызывать что тебе нужно чтоб компилер не ругался на сигнатуру :)
Спасибо сказали:
IMB
Сообщения: 2567
ОС: Debian

Re: C++: потоки

Сообщение IMB »

И чем это поможет?
Спасибо сказали:
Ramol
Сообщения: 44
ОС: Debian

Re: C++: потоки

Сообщение Ramol »

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

Код: Выделить всё

class A{
void operator()();
}

A a;
a();
Спасибо сказали: