Linux系统编程 多线程
Posted 蚍蜉撼树谈何易
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux系统编程 多线程相关的知识,希望对你有一定的参考价值。
多线程
初识线程
线程的概念
在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”。
一切进程至少都有一个执行线程
线程在进程内部运行,本质是在进程地址空间内运行
在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流
线程的优缺点
优点
创建一个新线程的代价要比创建一个新进程小得多。
与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多。
线程占用的资源要比进程少很多。
能充分利用多处理器的可并行数量。
在等待慢速I/O操作结束的同时,程序可执行其他的计算任务。
计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现。
I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
缺点
1.频繁的线程切换会导致系统运行效率降低。
2.健壮性(鲁棒性)相对于进程来说低。因为进程之间是独立的,而线程不具有这样的特性。一个线程崩溃,随之而来的便是整个进程的崩溃。
3.多个线程在访问同一变量时,可能会造成二义性问题。
了解pid(轻量级线程号)与tgid(线程组id)
tgid为进程号,pid为线程号
Lwp为轻量级进程(线程),相比于多进程来说,轻量级进程可以与主进程共用一份地址空间,与普通进程相比,LWP与其它进程共享所有(或大部分)逻辑地址空间和系统资源,一个进程可以创建多个LWP,这样它们共享大部分资源;LWP有它自己的进程标识符。
进程与线程概念解释
进程:是承担分配资源的基本实体,
线程:是调度的一个基本单位,线程是进程里面的一个执行流。
同一所属组下多线程之间共享与独立的空间
进程与线程的对比
1.从性质上来说:进程是承担资源分配的基本单位,而线程是cpu调度的一个基本单位
2.从特性上来说,进程具有独立性,而线程不具有独立性。
多进程与多线程对比
线程控制
线程的创建
1 #include<iostream>
2 #include<unistd.h>
3 #include<pthread.h>
4 using namespace std;
5 void *run(void *arg)
6
7 int *p=(int*)arg;
8 while(1)
9
10 cout<<"i am thread1,~~~~~"<<*p<<endl;
11 sleep(1);
12
13
14 int main()
15
16 pthread_t tid;
17 int i=10;
18 int ret=pthread_create(&tid,NULL,run,(void*)&i);
19 if(ret!=0)
20
21 return -1;
22
23 while(1)
24
25 cout<<"i am main thread"<<endl;
26 sleep(1);
27
28 return 0;
29
makefile
1 mythread:test_creat.cpp
2 g++ $^ -o $@ -lpthread
3 .PHONY:clean
4 clean:
5 rm -f mythread
线程入口的参数传递规则:
规则1:线程入口的参数不可以传递临时变量
规则2:传递的要是堆上开辟的空间,哪个线程用,哪个线程在用完后释放。
规则3:线程入口参数既可以传递内置类型,也可以传递自定义类型。
1 #include<iostream>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<pthread.h>
5 using namespace std;
6 class mythread
7
8 public:
9 mythread(int key)
10
11 i=key;
12
13 ~mythread()
14
15 void start()
16
17 int ret= pthread_create(&tid,NULL,run,this);
18 if(ret<0)
19
20 return ;
21
22
23 static void*run(void *arg)
24
25 mythread*pn=(mythread*)arg;
26 while(1)
27
28
29 cout<<"i am thread "<<pn->i<<endl;
30 sleep(1);
31
32
33
34 private:
35 pthread_t tid;
36 int i;
37 ;
38 int main()
39
40 mythread *rd=new mythread(5);
41 rd->start();
42 while(1)
43
44 cout<<" i am main thread"<<endl;
45 sleep(1);
46
47 delete rd;
48 return 0;
49
这个代码其实不是很好,因为没有考虑到让工作线程去释放。所以只供参考,不推荐写这样的代码
线程终止
常见线程退出的几种方式:
1: exit_thread.cpp ? ? ?? buffers
1 #include<iostream>
2 #include<pthread.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 using namespace std;
6 struct student
7
8 void set(int num)
9
10 age=num;
11
12 int age;
13 ;
14 void *run(void *arg)
15
16 student *pn=(student*)arg;
17 int count=0;
18 while(1)
19
20 if(count++==20)
21
22 // pthread_cancel(pthread_self());
23 // pthread_exit(NULL);
24 cout<<"我溜了,886"<<endl;
25 return NULL;
26
27 cout<<"i am work pthread "<< pn->age<<endl;
28
29
30 int main()
31
32
33 pthread_t tid;
34 student *pre=new student;
35 pre->set(20);
36 pthread_create(&tid,NULL,run,pre);
37 while(1)
38
39 cout<<"i am main thread"<<endl;
40 sleep(1);
41
42 return 0;
43
makefile
1 exit_thread:exit_thread.cpp
2 g++ $^ -o $@ -lpthread
3 .PHONY:clean
4 clean:
5 rm -f exit_thread
线程等待
1 #include<iostream>
2 #include<pthread.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 using namespace std;
6 struct student
7
8 void set(int num)
9
10 age=num;
11
12 int age;
13 ;
14 void *run(void *arg)
15
16 student *pn=(student*)arg;
17 int count=0;
18 while(1)
19
20 sleep(1); //先让它睡1s,发现主线程仍旧不打印,证明为阻塞等
21 if(count++==20)
22
23 // pthread_cancel(pthread_self());
24 // pthread_exit(NULL);
25 cout<<"我溜了,886"<<endl;
26 return NULL;
27
28 cout<<"i am work pthread "<< pn->age<<endl;
29
30
31 int main()
32
33
34 pthread_t tid;
35 student *pre=new student;
36 pre->set(20);
37 pthread_create(&tid,NULL,run,pre);
38 pthread_join(tid,NULL);
39 while(1)
40
41 cout<<"i am main thread"<<endl;
42 sleep(1);
43
44 return 0;
45
线程分离
1 #include<iostream>
2 #include<pthread.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 using namespace std;
6 struct student
7
8 void set(int num)
9
10 age=num;
11
12 int age;
13 ;
14 void *run(void *arg)
15
//自身分离
pthread_detach(pthread_self());
16 student *pn=(student*)arg;
17 int count=0;
18 while(1)
19
20 sleep(1); //先让它睡1s,发现主线程仍旧不打印,证明为阻塞等
21 if(count++==20)
22
23 // pthread_cancel(pthread_self());
24 // pthread_exit(NULL);
25 cout<<"我溜了,886"<<endl;
26 return NULL;
27
28 cout<<"i am work pthread "<< pn->age<<endl;
29
30
31 int main()
32
33
34 pthread_t tid;
35 student *pre=new student;
36 pre->set(20);
37 pthread_create(&tid,NULL,run,pre);
38 //pthread_join(tid,NULL);
//在主线程中设置分离属性
pthread_detach(tid);
39 while(1)
40
41 cout<<"i am main thread"<<endl;
42 sleep(1);
43
44 return 0;
45
线程安全
示例:
线程互斥:
互斥:任何时刻,互斥保证有且只有一个执行流进入临界区,访问临界资源,通常对临界区起保护作用
为什么需要?
假设有两个线程同时进入临界区去访问临界资源时,假如两个线程都想对这个变量做一个++操作的话,此时因为他们同时拿到了这份资源,在执行完++操作后,本该两个执行流的++操作,其实最后只起到了一个执行流的效果。所以,当我们对临界区中的临界资源进行修改时,必须保证其原子性。而且必须保证拥有临界资源的执行流有且只有一个,这样可以避免运算结果的二义性。
1 #include<iostream>
2 #include<stdio.h>
3 #include<unistd.h>
4 #include<pthread.h>
5 using namespace std;
6 const int num=4;
7 int ticket=1000;
W> 8 void * getticket(void *arg)
9
10
11 while(1)
12
13 if(ticket>0)
14
W> 15 printf("i am %p , i got a ticket %d\\n",pthread_self(),ticket);
16 --ticket;
17
18 else
19
20 break;
21
22
W> 23
24 int main()
25
26 pthread_t arr[num];
27 int i=0;
28 for(;i<num;++i)
29
30 int ret= pthread_create(&arr[i],NULL,getticket,NULL);
31 if(ret<0)
32
33 cout<<"create error"<<endl;
34 return -1;
35
36
37 i=0;
38 for(;i<num;++i)
39
40 pthread_join(arr[i],NULL);
41
42 return 0;
43
两个不同的人拿到了同一张票,这便是线程不安全的情况
锁的本质
加锁的时机
加锁的接口
利用加锁解决掉上面拿到同一张票问题
1 #include<iostream>
2 #include<stdio.h>
3 #include<unistd.h>
4 #include<pthread.h>
5 using namespace std;
6 const int num=4;
//定义一个全局变量的锁
7 pthread_mutex_t lock;
8 int ticket=1000;
W> 9 void * getticket(void *arg)
10
11
12 while(1)
13
//上锁
14 pthread_mutex_lock(&lock);
15 if(ticket>0)
16
W> 17 printf("i am %p , i got a ticket%d\\n",pthread_self(),ticket);
18 --ticket;
19
20 else
21
//确保在退出之前归还锁
22 pthread_mutex_unlock(&lock);
23 break;
24
//取完一次后也要归还锁
25 pthread_mutex_unlock(&lock);
26
27
W> 28
29 int main()
30
31 pthread_t arr[num];
32 int i=0;
//锁的初始化,动态初始化的,所以必须要释放该锁资源
33 pthread_mutex_init(&lock,NULL);
34 for(;i<num;++i)
35
36 int ret=pthread_create(&arr[i],NULL,getticket,NULL);
37 if(ret<0)
38 互斥锁和临界区有啥区别?