C++多线程编程第三讲--线程传参详解,detach()大坑,成员函数做线程函数

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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/*线程传参详解,detach()大坑,成员函数做线程函数*/
//(1)传递临时对象作为线程参数
    //(1.1)要避免的陷阱
    #include<iostream>
    #include<thread>
     
    using namespace std;
     
    void myprint(const int& i, const char* pmybuf)
    {
        cout << i << endl;                     //这个地方i的地址已经不是主线程中对应的地址了,也就是说虽然用的是引用传递的数据,
                                                //但是实际上是把主线程中的数据在内存中又复制了一份传递到了这个线程中。
        cout << pmybuf << endl;                 //这个地方用的数据的地址是与主线程中的数据的地址是相同的。
    }
     
     
    int main()
    {
        int myvar = 1;
        int& mvary = myvar;
        char mybuf[] = "ths is a test";
        //虽然函数参数i接受的是一个引用,但实际到了线程内是复制了一份数据进的线程
        //但是对应指针而言,是不会复制一份数据进入新的线程中的,所以这个地方用了detach,第二个参数
        //用了指针,是存在错误的,一旦主线程中的mybuf被释放掉,而新线程中还在用这个数据,就会出现错误
        thread mytobj(myprint, mvary, mybuf);
        if (mytobj.joinable())
        {
            mytobj.detach();
        }
     
        cout << "I Love China!" << endl;
     
        return 0;
    }
 
// 修改如下:
 
     #include<iostream>
    #include<thread>
    #include<string>
     
    using namespace std;
     
    void myprint(const int i, const string& pmybuf)
    {
        cout << i << endl;
        cout << pmybuf.c_str() << endl;
    }
     
     
    int main()
    {
        int myvar = 1;
        int& mvary = myvar;
        char  mybuf[] = "ths is a test";
     //这个地方存在一个char *转成string的一个隐式转换,转换的过程在新的线程执行的,
     //一旦主线程执行结束,这个过程还没有转换完成,新的线程中就接收不到数据,所以还是存在bug。
        thread mytobj(myprint, mvary, mybuf);  
        if (mytobj.joinable())
        {
            mytobj.detach();
        }
     
        cout << "I Love China!" << endl;
     
        return 0;
    }
 
// 上面的程序依然还有bug
// 我们需要再次修改
 
     #include<iostream>
    #include<thread>
    #include<string>
     
    using namespace std;
     
    void myprint(const int i, const string& pmybuf)
    {
        cout << i << endl;
        cout << pmybuf.c_str() << endl;
    }
     
     
    int main()
    {
        int myvar = 1;
        int& mvary = myvar;
        char  mybuf[] = "ths is a test";
        thread mytobj(myprint, mvary, string(mybuf));    //这种临时string的写法可以保证在新线程中可以获得一个有效的的数据,
        if (mytobj.joinable())
        {
            mytobj.detach();
        }
     
        cout << "I Love China!" << endl;
     
        return 0;
    }
 
// 此用法的验证
 
     #include<iostream>
    #include<thread>
    #include<string>
     
    using namespace std;
     
    class A
    {
    public:
        int m_i;
        //带参构造函数
        A(int a) :m_i(a)
        {
            cout << "[A::A(int a)构造函数执行]" << endl;
        }
     
        //拷贝构造函数
        A(const A& a) :m_i(a.m_i)
        {
            cout << "[A::A(const A& a)拷贝构造函数执行]" << endl;
        }
     
        ~A()
        {
            cout << "[A::~A()析构函数执行]" << endl;
        }
    };
     
    void myprint(const int i, const A& pmybuf)
    {
        cout << &pmybuf << endl;
    }
     
     
    int main()
    {
        int myvar = 1;
        int mysecondpar = 12;
        //经过实验得出,由数字mysecondpar转换成A的对象调用构造函数是在新的线程中执行的
        thread mytobj(myprint, myvar, mysecondpar);
        if (mytobj.joinable())
        {
            mytobj.detach();
        }
     
        cout << "I Love China!" << endl;
     
        return 0;
    }
 
// 修改:
 
     #include<iostream>
    #include<thread>
    #include<string>
     
    using namespace std;
     
    class A
    {
    public:
        int m_i;
        //带参构造函数
        A(int a) :m_i(a)
        {
            cout << "[A::A(int a)构造函数执行]" << endl;
        }
     
        //拷贝构造函数
        A(const A& a) :m_i(a.m_i)
        {
            cout << "[A::A(const A& a)拷贝构造函数执行]" << endl;
        }
     
        ~A()
        {
            cout << "[A::~A()析构函数执行]" << endl;
        }
    };
     
    void myprint(const int i, const A& pmybuf)
    {
        cout << &pmybuf << endl;
    }
     
     
    int main()
    {
        int myvar = 1;
        int mysecondpar = 12;
        //经过实验得出,这种临时转换会在主线程中执行,并且还会执行一份拷贝构造函数
        thread mytobj(myprint, myvar, A(mysecondpar));
        if (mytobj.joinable())
        {
            mytobj.detach();
        }
     
        cout << "I Love China!" << endl;
     
        return 0;
    }
 
//(1.2)总结
    // 自定义的类型尽量用引用来接受,否则会出现多次复制的情况,降低程序性能。
 
//(2)临时对象作为线程参数继续讲
    //(2.1)线程id的概念:每一个线程都对应一个id,std::this_thread::get_id()
//(3)传递类对象,智能指针作为线程参数
     #include<iostream>
    #include<thread>
    #include<string>
     
    using namespace std;
     
    class A
    {
    public:
        mutable int m_i;
        //带参构造函数
        A(int a) :m_i(a)
        {
            cout << "[A::A(int a)构造函数执行] " << this << " thread_id = " << std::this_thread::get_id() << endl;
        }
     
        //拷贝构造函数
        A(const A& a) :m_i(a.m_i)
        {
            cout << "[A::A(const A& a)拷贝构造函数执行] " << this << " thread_id = " << std::this_thread::get_id() << endl;
        }
     
        ~A()
        {
            cout << "[A::~A()析构函数执行] " << this << " thread_id = " << std::this_thread::get_id() << endl;
        }
    };
     
    void myprint2(const A& pmybuf)
    {
        pmybuf.m_i = 199;
        cout << "m_i =  :" << pmybuf.m_i << " this_thread id = " << std::this_thread::get_id() << endl;
    }
     
     
    int main()
    {
        int i = 100;
        A a(i);
     
        //即便myprint2的参数是一个引用,且在新的线程中修改了m_i的值,但是主线程中a的m_i的值依然没有改变
        thread mythread(myprint2, a);
     
        mythread.join();
        cout << a.m_i << endl;
     
        cout << "main thread end" << endl;
     
        return 0;
    }
 
// 如果想要修改主线程中的a的数据,那么需要用到std::ref()
 
     #include<iostream>
    #include<thread>
    #include<string>
     
    using namespace std;
     
    class A
    {
    public:
        int m_i;
        //带参构造函数
        A(int a) :m_i(a)
        {
            cout << "[A::A(int a)构造函数执行] " << this << " thread_id = " << std::this_thread::get_id() << endl;
        }
     
        //拷贝构造函数
        A(const A& a) :m_i(a.m_i)
        {
            cout << "[A::A(const A& a)拷贝构造函数执行] " << this << " thread_id = " << std::this_thread::get_id() << endl;
        }
     
        ~A()
        {
            cout << "[A::~A()析构函数执行] " << this << " thread_id = " << std::this_thread::get_id() << endl;
        }
    };
     
    void myprint2(A& pmybuf)
    {
        pmybuf.m_i = 199;
        cout << "m_i =  :" << pmybuf.m_i << " this_thread id = " << std::this_thread::get_id() << endl;
    }
     
     
    int main()
    {
        int i = 100;
        A a(i);
     
        //使用了ref函数,则新线程中值的修改也影响到了主线程的值
        thread mythread(myprint2, std::ref(a));
     
        mythread.join();
        cout << a.m_i << endl;
     
        cout << "main thread end" << endl;
     
        return 0;
    }<br>
     #include<iostream>
    #include<thread>
    #include<string>
    #include<memory>
     
    using namespace std;
     
    void myprint2(unique_ptr<int> iptr)
    {
        cout << "iptr =  :" << *iptr << " this_thread id = " << std::this_thread::get_id() << endl;
    }
     
     
    int main()
    {
        unique_ptr<int> iptr(new int(100));
     
        thread mythread(myprint2, std::move(iptr));
        mythread.join();
     
     
        return 0;
    }
 
//(4)用成员函数指针做线程函数
#include<iostream>
#include<thread>
#include<string>
#include<memory>
 
using namespace std;
 
class A
{
public:
    int m_i;
    //带参构造函数
    A(int a) :m_i(a)
    {
        cout << "[A::A(int a)构造函数执行] " << this << " thread_id = " << std::this_thread::get_id() << endl;
    }
 
    //拷贝构造函数
    A(const A& a) :m_i(a.m_i)
    {
        cout << "[A::A(const A& a)拷贝构造函数执行] " << this << " thread_id = " << std::this_thread::get_id() << endl;
    }
 
    ~A()
    {
        cout << "[A::~A()析构函数执行] " << this << " thread_id = " << std::this_thread::get_id() << endl;
    }
 
    void thread_work(int num)
    {
        cout << "num = " << num << " thread id = " << std::this_thread::get_id() << endl;
    }
 
    void operator()(int num)
    {
        cout << "num = " << num << " thread id = " << std::this_thread::get_id() << endl;
    }
};
 
 
int main()
{
    int i = 100;
    A a(i);
 
    thread mytobj(&A::thread_work, a, i);
    mytobj.join();
 
 
    A a1(100);
    thread mytobj2(a1, 10);
    mytobj2.join();
 
    return 0;
}

  

posted on   xcxfury001  阅读(170)  评论(0编辑  收藏  举报

编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用

导航

< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示