函数原型
#include <pthread.h>
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*), void *restrict arg);
创建线程的函数有四个参数,第一个参数是一个pthread_t类型的指针。第二个参数通常设置为NULL,第三个参数是新创建的线程的函数,返回类型为(void *),第四个参数为传递的参数,类型为(void *)类型。
下面给出一个实例
#include<pthread.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<time.h>
void * xiancheng(void *p)
{
pid_t pid;
pthread_t tid;
pid=getpid();
tid=pthread_self();
printf("%s jinchengwei %u xianchengwei %u\n",(char *)p,(unsigned int)pid,(unsigned int)tid);
return (void *)0;
}
int main()
{
pthread_t id;
int err=pthread_create(&id,NULL,xiancheng,"hello");
if(err!=0)
{
printf("error\n");
return 0;
}
xiancheng("zhi");
sleep(1);
}
使用命令gcc pthreap.c - o pthreap -lpthreap进行编译
在这里我们需要注意到几个点
- 我们创建线程使用的函数的第一个参数id,我们传递的是一个地址,这个参数的作用便是将这个线程的tid返回给id这个变量中。
- 这个函数的第三个参数是一个函数的名字,表示一个新创建的线程该执行的函数,这个函数的返回类型为(void *),因此我们在创建函数时应该使返回值为(void *)类型。
- 第四个参数中,我们传递一个(void *)类型的变量给这个新的线程,因此我们在函数接收这个变量时应该声明为(void *)类型。
- pthread_t和pid_t的定义类型为 typedef unsigned pthread_t和typedef unsigned pid_t。
- 这个函数如果出错,出错的信息不会保存在errno中,而是这个函数的返回值,成功返回0。
- 在线程函数中如果使用exit函数,便会结束整个进程。
结束子线程
线程的结束有几种方式
- 使用pthread_exit来本结束线程
- 使用pthread_cancel来结束本进程的一个线程,有一个参数为被结束的线程的id
- 使用return来结束线程
下面我们来介绍一下线程的返回值
线程的返回值和结束线程的三种方式相对应,也是有三种 - 使用pthread_exit((void *)1)来进行结束,使用pthread_join函数来进行接收这个线程的返回值pthread_join(id,&a)其中a来接收1的值。a的定义为void * a;
- 使用return和上面的一样
- 使用pthread_cancel来结束,会返回一个宏定义PTHREAD_CANCELED,这个定义为-1。
下面进行一个实例
#include<pthread.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<time.h>
void * xiancheng(void *argv)
{
return (void *)0;
}
void * xiancheng1(void *argv)
{
pthread_exit((void *)1);
}
void * xiancheng2(void *argv)
{
while(1)
{
sleep(1);
printf("waitting 4s\n");
}
}
int main()
{
pthread_t id;
void * a;
pthread_create(&id,NULL,xiancheng,NULL);
pthread_join(id,&a);
printf("%d\n",(int)a);
pthread_create(&id,NULL,xiancheng1,NULL);
pthread_join(id,&a);
printf("%d\n",(int)a);
pthread_create(&id,NULL,xiancheng2,NULL);
sleep(3);
pthread_cancel(id);
pthread_join(id,&a);
printf("%d\n",(int)a);
}
结果:
root@ubuntu:/home/sun/project# ./aa
0
1
waitting 4s
waitting 4s
-1
线程间的同步
当有多个线程同时访问一个共享变量时便会出现问题,可能并不是我们预期的结果出现,接下来我们分析一下原因。
代码实例
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/types.h>
int count=0;
void *pthread1(void * argc)
{
int i=0;
for(i=0;i<100000;++i)
count++;
return (void *)0;
}
void *pthread2(void * argc)
{
int i=0;
for(i=0;i<100000;++i)
count++;
return (void *)0;
}
int main()
{
pthread_t tid1,tid2;
pthread_create(&tid2,NULL,pthread1,NULL);
pthread_create(&tid2,NULL,pthread2,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
printf("%d",count);
return 0;
}
运行代码结果
在单核上运行结果
root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project#
在多核上运行结果
root@ubuntu:/home/sun/project# ./aa
111388root@ubuntu:/home/sun/project# ./aa
127061root@ubuntu:/home/sun/project# ./aa
108070root@ubuntu:/home/sun/project# ./aa
101543root@ubuntu:/home/sun/project# ./aa
141164root@ubuntu:/home/sun/project# ./aa
200000root@ubuntu:/home/sun/project# ./aa
129528root@ubuntu:/home/sun/project# ./aa
148950root@ubuntu:/home/sun/project# ./aa
122621root@ubuntu:/home/sun/project# ./aa
109718root@ubuntu:/home/sun/project# ./aa
157505root@ubuntu:/home/sun/project# ./aa
183777root@ubuntu:/home/sun/project#
分析
可以看到我们使用多线程访问一个共享变量时在多核上回出现错误,我们分析一下出现错误的原因。
正常的程序运行顺序
- 程序在运行时先从内存上取出数据到寄存器上
- 然后执行加1操作
- 再将数据存如内存
多线程也是以这样的方式运行的,但是在多核上会同时运行这个进程的多个线程,比如线程1从内存取出a=5,开始执行a++操作执行10次,执行后a=15,线程2也开始执行a++操作,由于线程2在执行时线程1还没有执行完,表示还没有将15写入内存,因此线程2取出a=5,因此线程2便会10次a++操作,执行完后a=15,将15写入内存,因此线程1和线程2都会将15写入内存,内存的最终结果为15,但是线程1和线程2一共执行了30次加法操作,因此便会出现小于预期结果的值。
网友评论