这里主要演示线程的以下几个特性
- 线程共享同一地址空间(全局变量,堆,栈)
- 线程退出引起进程退出
- 多线程充分利用多核CPU资源
同组线程共用同一地址空间
- 全局变量
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
//1.共享全局区资源
int g_count = 0;
void* ThreadEntry1(void* arg)
{
(void)arg;
while(1)
{
++g_count;
printf("t1 : %d\n",g_count);
sleep(1);
}
return NULL;
}
void* ThreadEntry2(void* arg)
{
(void)arg;
while(1)
{
++g_count;
printf("t2 : %d\n",g_count);
sleep(1);
}
return NULL;
}
int main()
{
pthread_t t1,t2;
pthread_create(&t1,NULL,ThreadEntry1,NULL);
pthread_create(&t2,NULL,ThreadEntry2,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return 0;
}
上面代码中,有一个全局变量 g_count,两个线程,同时尝试去对 全局变量 g_count 进行修改
结果演示:
根据运行结果,我们可以看到,同组线程对全局变量是共享的
- 堆上的变量
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
//2.共享堆上的资源
void* ThreadEntry1(void* arg)
{
int* p = (int*)arg;
while(1)
{
++(*p);
printf("t1 : %d\n",*p);
sleep(1);
}
return NULL;
}
void* ThreadEntry2(void* arg)
{
int* p = (int*)arg;
while(1)
{
++(*p);
printf("t2 : %d\n",*p);
sleep(1);
}
return NULL;
}
int main()
{
int* p = (int*)malloc(sizeof(int));
pthread_t t1,t2;
pthread_create(&t1,NULL,ThreadEntry1,p);
pthread_create(&t2,NULL,ThreadEntry2,p);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return 0;
}
上面代码中,在main函数中malloc出了一段空间,这段空间在堆上,线程1和2同时去修改这个堆上的变量。
- 栈上的变量
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
//3.共享线程栈
// 使用栈上变量比较危险,我们使用前必须保证变量存在
// 但是主线程比较安全,因为他存在于整个进程
void* ThreadEntry1(void* arg)
{
int* p = (int*)arg;
while(1)
{
++(*p);
printf("t1 : %d\n",*p);
sleep(1);
}
return NULL;
}
void* ThreadEntry2(void* arg)
{
int* p = (int*)arg;
while(1)
{
++(*p);
printf("t2 : %d\n",*p);
sleep(1);
}
return NULL;
}
int main()
{
int a = 0;
int* p = &a;
pthread_t t1,t2;
pthread_create(&t1,NULL,ThreadEntry1,p);
pthread_create(&t2,NULL,ThreadEntry2,p);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return 0;
}
代码中将主线程栈上一个变量a传给两个线程的线程入口函数,线程1和2分别随主线程栈上的元素进行改变。
注意:
修改栈上的资源是比较危险的,因为我们说过,每个线程都有自己的一个栈,这个但是这个栈是可以被访问也可以被改变的,试想,如果线程1已经退出了,线程2尝试去修改线程1栈上的元素,这是就很可能造成访问越界!但是,访问主线程栈上的元素相对比较安全,因为主线程栈是大家所共享的。
线程退出引起进程退出
线程组中某一个线程访存异常,都会导致进程异常终止。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
//解引用空指针
//会导致硬件设备MMU发现这是一个异常操作
//会给操作系统发送一个异常
//操作系统内核就会给进程发送11号信号,导致进程异常终止 - 段错误
//解引用空指针使线程异常终止,观察
//线程一旦异常终止,就会导致整个进程都结束掉
void* ThreadEntry1(void* arg)
{
int* p = (int*)arg;
while(1)
{
++(*p);
printf("t1 : %d\n",*p);
int *ptr = NULL;
*ptr = 10;
sleep(1);
}
return NULL;
}
void* ThreadEntry2(void* arg)
{
int* p = (int*)arg;
while(1)
{
++(*p);
printf("t2 : %d\n",*p);
sleep(1);
}
return NULL;
}
int main()
{
int a = 0;
int* p = &a;
pthread_t t1,t2;
pthread_create(&t1,NULL,ThreadEntry1,p);
pthread_create(&t2,NULL,ThreadEntry2,p);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return 0;
}
线程尝试对空指针解引用时,进程终止。
多线程充分利用多核CPU资源
- 场景1,创建对个线程不停歇工作,使用top指令查看CPU利用率
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
//利用top指令查看cpu占用率
void* ThreadEntry(void* arg)
{
(void)arg;
while(1)
{}
return NULL;
}
int main()
{
pthread_t tid[32];
int n = 4;
int i = 0;
for(; i < n; ++i)
{
pthread_create(&tid[i],NULL,ThreadEntry,NULL);
}
for(i = 0; i < n; ++i)
{
pthread_join(tid[i],NULL);
}
return 0;
}
创建两个线程,这两个线程一直在死循环(可换成具体场景)。
如上图观察,CPU占用率可高达400%(四核CPU),证明我们充分利用了CPU资源。