赞
踩
目录
在LINUX内核中是不存在线程的概念的,只有轻量级进程的概念,但是在用户的角度来看,用户就是要把轻量级进程当做线程的使用,所以将轻量级进程的控制接口,封装成了一个线程库,用户直接使用库,也就屏蔽了底层的实现。
所以在g++编译的时候记得 加上 -lpthread选项,让可执行程序链接ptread库
创建一个新的线程
参数thread,是输出型参数,创建进程的的id。
参数attr是一个包含创建时间,属性的结构体,用于初始化新创建的线程,设置为NULL采用默认初始化。
start_routine是一个函数指针,线程会执行函数的内容。
arg是start_routine的参数。
- #include <iostream>
- #include <pthread.h>
- #include <sys/types.h>
- #include <unistd.h>
- using namespace std;
- void *start_routine(void *v)
- {
- while (true)
- {
- cout << "I am new thread pid: " << getpid() << endl;
- sleep(1);
- }
- }
-
- int main()
- {
- pthread_t tid;
- pthread_create(&tid, nullptr, &start_routine, nullptr);
- while (true)
- {
- cout << "I am main thread pid:" << getpid() << endl;
- sleep(1);
- }
-
- return 0;
- }
我们发现这两线程的pid是同一个,这是因为他们属于同一个进程。
那操作系统调度的时候怎么区分他们两个呢?
别忘了,进程和线程在liunx下都叫轻量级进程LWP
让我们使用 ps -aL 选项查看进程的所有轻量级进程。
我们发现有两个LWP449636,449637,其中449636就是main函数的执行流,也就是说当进程内部之有一个执行流的时候PID==LWP。
关联一个结束的进程
参数thread是线程的id,这个id来源自pthread库,而不是系统内核的LWP,他们的关系是一 一对应的。
参数retval是一个输出型参数,用于保存目标线程的退出值。
线程退出是不可以使用exit的,因为exit是给进程用的,使用exit直接整个进程就干掉了
1.return可以退出线程。
2.
终止调用的线程。
3.
终止参数thread线程
5.demo代码
v1
- #include <iostream>
- #include <pthread.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <vector>
- #include <string>
- #include <cstdio>
- #include <cstdlib>
- using namespace std;
-
- // 创建5个进程,分别让他们执行任务,获取返回的结果
-
- enum
- {
- sum = 5
- };
-
- void *handler(void *arg)
- {
- char* threadname = static_cast<char*>(arg);
- cout << "I am: " << threadname << endl;
- delete[] threadname;
- return (void *)1;
- }
-
- int main()
- {
- // 创建线程
- vector<pthread_t> tids;
- for (int i = 0; i < sum; i++)
- {
- sleep(1);
- char *buffer = new char[64];
- pthread_t tid;
- snprintf(buffer, 64, "thread %d", i + 1);
- pthread_create(&tid, nullptr, handler, buffer);
- tids.push_back(tid);
- }
-
- for (auto &t : tids)
- {
- void *ret;
- int n = pthread_join(t, &ret);
- cout << "main thread get ret:" << (long long)ret << " " << "n:" << n << endl;
- }
- return 0;
- }
v2
别忘了pthread_create最后一个参数类型是void* 这意味着可以和c++的类结合起来,不仅仅是传字符串,传整形,同样返回也可以使用类。
- #include <iostream>
- #include <pthread.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <vector>
- #include <string>
- #include <cstdio>
- using namespace std;
-
- // 创建5个进程,分别让他们执行任务,获取返回的结果
-
- enum
- {
- sum = 5
- };
- class result//储存结果
- {
- public:
- result()
- {
- }
- result(const string &threadname, int ret)
- : _threadname(threadname)
- , _ret(ret)
- {
-
- }
- void print()
- {
- cout << "I am " << _threadname << "added:" << _ret << endl;
- }
-
- private:
- string _threadname;
- int _ret;
- };
-
- class doTask//执行任务
- {
- public:
- doTask()
- {
- }
- doTask(int x, int y)
- : _x(x), _y(y)
- {
- }
-
- int run()
- {
- return _x + _y;
- }
-
- private:
- int _x;
- int _y;
- };
-
- class SentTask//发送任务
- {
- public:
- SentTask()
- {
- }
- SentTask(string &threadname, int x, int y)
- : _threadname(threadname)
- , dt(x, y)
- {
- }
- int Exute()
- {
- return dt.run();
- }
-
- string threadname()
- {
- return _threadname;
- }
-
- private:
- string _threadname;
- doTask dt;
- };
-
- void *handler(void *args)
- {
- SentTask *pst = static_cast<SentTask *>(args);
- result *ret = new result(pst->threadname(), pst->Exute());
- delete pst;
- return ret;
- }
-
- int main()
- {
- vector<pthread_t> tids;
- for (int i = 0; i < sum; i++)
- {
- pthread_t tid;
- char buffer[64];
- snprintf(buffer, 64, "thread %d", i + 1);
- string threadname = buffer;
- SentTask *st = new SentTask(threadname, i, 3);
- pthread_create(&tid, nullptr, handler, st);
- tids.push_back(tid);
- }
-
- for (auto t : tids)
- {
- void *ret;
- pthread_join(t, &ret);
- result *tmp = static_cast<result *>(ret);
- tmp->print();
- delete tmp;
- }
-
- return 0;
- }
1.创建一个线程的代价比进程小的很多,创建一个线程就创建pcb,不用创建地址空间页表。
2.操作系统切换线程的工作,要比进程小的很多。如何理解呢?进程切换和线程切换相比,无非就是多了切换几个寄存器的工作呗,能有多大消耗呢?
但是别忘了还有缓存命中这种东西,cpu在加载代码的时候,会把该代码附近几行也加载到cpu的高速缓存中,一旦进程切换了,这些高速缓存的数据,对于下个进程就没有用了,都是失效的数据,但是对线程不是这样的,线程代码和全局数据是共享的。
如果线程过多也是会有性能损失,因为线程来回切换也需要成本,在计算密集型应用线程数不要大于cpu核数。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。