How to start and stop threads in a class member ?

How do you launch a thread in a member function.

1
2
3
4
5
6
7
8
9
10
11
12
  class Robot {
public:
    
    Robot() {};

    int idle_and_scan(int){
      cout << "idle "<< endl;
    }
    void run_state() {
 
    }
};


I need to start a thread running Robot::idle_and_scan from class Behavior.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Behavior {
public:
Behavior(){};
void run_thread() {
        //? thread_list.push_back(thread(&Robot::idle_and_scan);
        //How do I start a thread here? 
}    

void stop_thread() {
        //How do I stop the thread here? 
}

 std::vector<std::thread> thread_list;
}


Thanks

Chris
Alright this shouldn't be in the beginners section. Probably should be in General C++ Programming. I mean threads in beginners section? And I don't know but I'm telling you how to get answers.
Hello fellow beginner. I think I got it figured out. I'm trying to implement adding "listeners" (like the listeners in tekkotsu state machine) to move_and_scan that can influence (stop) its behavior. Is there a better way?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include <cstdlib>
#include <memory>
#include <thread>
#include <iostream>
#include <vector>
#include <atomic>

std::atomic<bool> status(1);

class Robot {
public:
    
    Robot() {};

    int keyboard_interrupt(int x){
      std::cout << "Keyboard running "<< std::endl;
      int c;
        for (;;) {
            if (!status)
                break;
            c=getchar();
            if (c == 'c') {
                std::cout << "Key c pressed, stopping move_and_scan" << std::endl;
                status=false;
                break;
            }
        }
      std::cout << "Keyboard interrupt ending" << std::endl;
    }
    
    int speech_interrupt(int x){
      std::cout << "Speech running "<< std::endl;
      int c;
        for (;;) {
            if (!status)
                break;
            
            }
            //if speech detected
                //status=false;
      std::cout << "Speech interrupt ending" << std::endl;
    }
    
    int move_and_scan(int x){
        std::cout << "Move running" << std::endl;
        for (;;) {
            if (!status) 
                break;
            
        }
        
        std::cout << "Move stopping" << std::endl;
    }
    
};

class Behavior {
public:
Behavior(Robot &o):r(o){};
void run_thread() {
    thread_list.push_back(std::thread(&Robot::keyboard_interrupt,r,0));
    thread_list.push_back(std::thread(&Robot::move_and_scan,r,0));
    thread_list.push_back(std::thread(&Robot::speech_interrupt,r,0));
    for ( auto &l: thread_list ) {
           l.join();
    }
    
}    


 std::vector<std::thread> thread_list;
 Robot &r;
};




int main(int argc, char** argv) {
    Robot create;
    
    Behavior c(create);
    c.run_thread();
    
    
    
    return 0;
}


Perhaps my next question will be in the General C++programming.
Thanks
Chris
Ok I don't know what you want to do because it's a bit hazy ("listener"???). But maybe you should mark this problem as solved and post on General C++ Programming because there are people that can answer your question better on there than here.

P.S. The more complex your questions the less likely people answer. Not that they can't but often because they don't want to take the time. So your questions should be as simple as possible, specific, and in a way so it's easy to answer.
Last edited on
A "listener" waits for an event and when such event happens (keypress, voice input, sonar obstacle detected), it modifies the running behavior. That's why I think it needs to run in a parallel thread. Otherwise the other way would be to "chunk" the execution of the behavior ( in the above example, moving in a straight line for a certain distance, chunking means using a loop that polls sensors internally in behavior ) and in series with the "chunks" listen for events and react. The chunking is what I've been doing until I decided that some modularity would be better. That is to say, I wanted to be able to add as many "event listeners" as I wish running in parallel with a behavior and be able to turn them on and off without changing too much the inner workings of a behavior and maybe even simplify it ( in this case by removing the internal sensor polling) .

My question has become a general one now that I have found an implementation that is working. My question merely is "Is there a better way" because this is my first time in thread programming and I am sure there are other ways available that I simply am not aware of due to my lack of exposure to these concepts. One of my earlier posts inspired me to change from using old C style dynamic memory allocation ( the only way I knew to solve the problem at the time) to using STL containers ( i found out that the STL containers did their own memory alloc and dealloc) and then eventually to shared_ptr for pointers that I would have otherwise allocated with new. A major design change, and that question was simply about deleting a 2d array. Little nuggets of information such as using auto and decltype were a bonus.

I am not aware of any "pitfalls" or bad programming practices that I may have coded above that may lead to future problems. Such concepts as type safety, stack unwinding, exceptions are above my head yet at this point. This is just a test program that I intend to integrate into my big project. I might very well run into problems integrating it and possibly have to change the implementation and have further questions. If I don't get a response in a reasonable time, and I am successful with the integration, I will definitely mark it as solved. I definitely agree (now) that this is actually an advanced concept that is better suited to go in another forum. But I still consider myself a beginner.

Thanks
Chris
@cmisip
Well I'm 2x more of a beginner than you. I do wish I knew how to do more than one thing at once. (ex: checking for keypresses whilst doing something else).
@boost lexical cast and @gentleguy. This is a long road of learning but its fun to travel down it. Seems like your own current programming challenge can be solved by a permutation of the above code.

I'm running into a snag. I am trying to create a thread_item list that has a mapping of thread id's (std::string) and an int value to a thread. I want to use the int value to signal the threads to terminate by passing a reference to the thread via std::ref(l_flag). If the value of l_flag changes to 0, it will signal the threads to exit. However, I cant seem to be able to pass the l_flag to the functions running in the threads.

I modifed the code to reflect more the mechanics of how my project is structured.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#include <cstdlib>
#include <memory>
#include <thread>
#include <deque>
#include <iostream>
#include <vector>
#include <atomic>
#include <unistd.h>

#include <mutex>
#include <condition_variable>

//bool behavior_is_running=true;

class Robot;

typedef int(Robot::*m_behavior)(int);
typedef int(Robot::*m_listener)(int&);

struct cancelled_error {};
class thread_item;

class Behavior {
public:
Behavior(Robot &o, m_behavior b_have, std::string i_id): r(o), main_b(b_have),id(i_id) {};

void run_listeners();

int exec(int flag);

m_behavior main_b;
std::string id;
std::vector<thread_item> thread_list;
Robot &r;
};





struct State {
    std::shared_ptr<Behavior> current_behavior;
    int current_param;
};


class Stack {
    public:
    Stack() {};
    void empty(){
         state.clear();
    };
    
    State pop_out(){  //always pop from front
        State last=state.front();
        state.pop_front();
        return last;
    };
    
    void push_b(std::shared_ptr<Behavior> i_behavior, int iparam) {
        State temp;
             temp.current_behavior=i_behavior;
             temp.current_param=iparam;        
             state.push_back(temp);
    };
    
    
    void push_f(std::shared_ptr<Behavior> i_behavior, int iparam) {
        State temp;
              temp.current_behavior=i_behavior;
              temp.current_param=iparam;        
	      state.push_front(temp);
    };
    
    std::deque<State> state;
};



class Robot {
public:
    
    Robot() {  
    std::shared_ptr<Behavior> move=std::make_shared<Behavior>(*this,&Robot::move_and_scan,"M");
    run_stack->push_b(move,10);
    };

    int keyboard_interrupt(int &x){
      std::cout << "Keyboard running "<< x <<std::endl;  //PROBLEM here, x gives garbage values
      int c;
        for (;;) {
            if (x!=0)
                break;
            /*c=getchar();
            if (c == 'c') {
                std::cout << "Key c pressed, stopping move_and_scan" << std::endl;
                status=false;
                break;
            }*/
        }
      std::cout << "Keyboard interrupt ending" << std::endl;
    }
    
    int speech_interrupt(int &x){
      std::cout << "Speech running "<< x <<  std::endl;  //PROBLEM HERE, x gives  garbage values
      int c;
        for (;;) {
            if (x!=0)
                break;
            
            }
            //if speech detected
                //status=false;
      std::cout << "Speech interrupt ending" << std::endl;
    }
    
    int move_and_scan(int x){
        std::cout << "Move starting " << std::endl;
        
        usleep(x*1000000);
        
        std::cout << "Move stopping" << std::endl;
    }
    
    void run_state() {
           if (run_stack->state.size() > 0) {
                std::cout << "Run stack size is  " << run_stack->state.size() << std::endl;
                s=run_stack->pop_out();
                
                int outcome=s.current_behavior->exec(s.current_param);
           }
           
           
    }
           
           std::shared_ptr<Stack> run_stack=std::make_shared<Stack>();
           State s;
           
};


class thread_item {
    public:
        thread_item(Robot &o, m_listener m_list, std::string i_id) : r(o), id(i_id) {
            thr=std::thread(m_list,r,std::ref(l_flag));  //PROBLEM HERE, std::ref not passing the correct values
            std::cout << "Listener flag " << l_flag << std::endl;
        }
        
        std::string id;
        int l_flag=1;
        std::thread thr;
        Robot &r;
    
};


void Behavior::run_listeners() {
    thread_list.push_back(thread_item(r,&Robot::keyboard_interrupt,"KL"));
    thread_list.push_back(thread_item(r,&Robot::speech_interrupt,"SL"));
}


int Behavior::exec(int flag){

       //NEED TO TURN ON THE LISTENERS HERE by setting l_flag to 1 (constructor default)
       run_listeners();
       
       std::cout << "Executing behavior "  << this->id << std::endl;  
       
       bool result = (r.*(this->main_b))(flag);
       
       for ( auto &l: thread_list ) {
       //TURN OFF THE LISTENERS HERE by setting l_flag to 0
           l.l_flag=0;
           l.thr.join();
          
          
      }
      
       
} 








int main(int argc, char** argv) {
    Robot create;
    
    //for (;;) {
        create.run_state();
    //}
    
    return 0;
}



Thanks
Chris
Out of my knowledge.... unless you want to use global variables. Sorry.

I'm not sure but what about using std::async?
Last edited on
I managed to fix the code and it seems to be working just fine. This is the second time that I've run into a situation that I cannot explain ( undefined behavior ) that was fixed simply by using shared_ptr. I turned the thread_list into a vector of shared_ptrs and that fixed the problem. Wether or not there are any memory leaks here, I am not sure but for the moment it seems to be doing what I want it to do. Now, the listeners could modify behavior_status which is being monitored by move_and_scan. If the listeners change it to false, move_and_scan quits with a status of 0. If no keypress is entered, move_and_scan proceeds to its successful termination with status of 1. At that point, the listeners are taken down by changing their controlling l_flag to 0 ( the listeners are monitoring the l_flag passed by reference into their respective functions ).

Incidentally, I found a nonblocking keypress detection code from :
http://cboard.cprogramming.com/c-programming/63166-kbhit-linux.html

I integrated it into the code to change the blocking keyboard i/o part. I thought you guys might be interested in that solution. I think I've had enough coding for tonight. Will finalize integration in the next few days and hopefully not run into any more snages. Code follows::

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#include <cstdlib>
#include <memory>
#include <thread>
#include <deque>
#include <iostream>
#include <vector>
#include <atomic>
#include <unistd.h>

#include <ctime>


#include <termios.h>
#include <unistd.h>
#include <fcntl.h>

int kbhit(void)
{
  struct termios oldt, newt;
  int ch;
  int oldf;
 
  tcgetattr(STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
  fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
 
  ch = getchar();
 
  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  fcntl(STDIN_FILENO, F_SETFL, oldf);
 
  if(ch != EOF)
  {
    ungetc(ch, stdin);
    return 1;
  }
 
  return 0;
}

class Robot;

typedef int(Robot::*m_behavior)(int);
typedef int(Robot::*m_listener)(int&);

struct cancelled_error {};
class thread_item;

class Behavior {
public:
Behavior(Robot &o, m_behavior b_have, std::string i_id): r(o), main_b(b_have),id(i_id) {};

void run_listeners();

int exec(int flag);

m_behavior main_b;
std::string id;
std::vector<std::shared_ptr<thread_item> >thread_list;
Robot &r;
};

struct State {
    std::shared_ptr<Behavior> current_behavior;
    int current_param;
};


class Stack {
    public:
    Stack() {};
    void empty(){
         state.clear();
    };
    
    State pop_out(){  //always pop from front
        State last=state.front();
        state.pop_front();
        return last;
    };
    
    void push_b(std::shared_ptr<Behavior> i_behavior, int iparam) { 
        State temp;
             temp.current_behavior=i_behavior;
             temp.current_param=iparam;        
             state.push_back(temp);
    };
    
    
    void push_f(std::shared_ptr<Behavior> i_behavior, int iparam) { 
        State temp;
              temp.current_behavior=i_behavior;
              temp.current_param=iparam;        
	      state.push_front(temp);
    };
    
    std::deque<State> state;
};



class Robot {
public:
    static std::atomic<bool> behavior_status;
    Robot() {  
    std::shared_ptr<Behavior> move=std::make_shared<Behavior>(*this,&Robot::move_and_scan,"M");
    run_stack->push_b(move,20);
    };

    int keyboard_interrupt(int &x){
      std::cout << "Keyboard running "<< x <<std::endl;
      int c;
        for (;;) {
            if (x!=1)
                break;
            
            if (kbhit()) {  
               if (getchar() == 'c') {
                  std::cout << "Key c pressed, stopping move_and_scan" << std::endl;
                  behavior_status=false;
               }
            }   
        }
      std::cout << "Keyboard interrupt ending" << std::endl;
    }
    
    int speech_interrupt(int &x){
      std::cout << "Speech running "<< x <<  std::endl;
      int c;
        for (;;) {
            if (x!=1)
                break;
            
            }
            //if speech detected
                //status=false;
      std::cout << "Speech interrupt ending" << std::endl;
    }
    
    int move_and_scan(int x){
        bool status=true;
        std::cout << "Move starting " << std::endl;
        behavior_status=true;
        int start_time=clock();
        int elapsed_time=0;
        do {
            if (!behavior_status) {
                std::cout << "Moved was stopped " << std::endl;
                status=false;
                break;
            }
            elapsed_time=clock() - start_time;
        } while (elapsed_time < (x*1000000));
        std::cout << "Move stopping with result "<< status << std::endl;
    }
    
    void run_state() {
           if (run_stack->state.size() > 0) {
                std::cout << "Run stack size is  " << run_stack->state.size() << std::endl;
                s=run_stack->pop_out();
                int outcome=s.current_behavior->exec(s.current_param);
           }
    }
           std::shared_ptr<Stack> run_stack=std::make_shared<Stack>();
           State s;
};

std::atomic<bool> Robot::behavior_status{0};

class thread_item {
    public:
        thread_item(Robot &o, m_listener m_list, std::string i_id) : r(o), id(i_id) {
            thr=std::make_shared<std::thread>(m_list,r,std::ref(l_flag));
        }
        
        std::string id;
        int l_flag=1;
        std::shared_ptr<std::thread> thr;
        Robot &r;
    
};


void Behavior::run_listeners() {
    std::shared_ptr<thread_item>m=std::make_shared<thread_item>(r,&Robot::keyboard_interrupt,"KL");
    thread_list.push_back(m);
    std::shared_ptr<thread_item>x=std::make_shared<thread_item>(r,&Robot::speech_interrupt,"KL");
    thread_list.push_back(x);
   
}


int Behavior::exec(int flag){
       run_listeners();
       std::cout << "Executing behavior "  << this->id << std::endl;  
       
       bool result = (r.*(this->main_b))(flag);
       
       for ( auto &l: thread_list ) {
           l->l_flag=0;
       }
       
       for ( auto &l: thread_list ) {
         l->thr->join();
       }
} 

int main(int argc, char** argv) {
    Robot create;
    
    //for (;;) {
        create.run_state();
    //}
    
    return 0;
}

@gentleguy
I thought you would know that I knew that too... by the way I wrote my last post.
Out of my knowledge.... unless you want to use global variables. Sorry.

It was phrased with that "..." and using the word "unless" which shows I don't think it's a good idea.
Why do I have to elaborate on this?
Last edited on
Successfully integrated. Although I am still perplexed as to why std::ref failed before I stated using shared_ptr's. I posted a new question about the issue in General C++ programming.

Thanks for your time and interest
Chris
The question with regards to std::ref was answered in this post:

http://www.cplusplus.com/forum/general/196689/

Basically, in the original non-working code, thread_item objects were temporary and destroyed when run_listeners finishes, but the reference to l_flag (invalid after run_listeners finishes) is being used by keyboard_interrupt and speech_interrupt. The solution was to use smart pointers as this extended the life of l_flag past run_listeners.

Chris

Topic archived. No new replies allowed.