美文网首页
线程的基本操作

线程的基本操作

作者: suntwo | 来源:发表于2019-06-17 16:52 被阅读0次

函数原型

#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次加法操作,因此便会出现小于预期结果的值。

相关文章

  • 线程的基本操作

    线程的基本操作 •线程状态切换 •终止线程(stop) •中断线程(interrupt) •挂起(suspend)...

  • 线程的基本操作

    线程的创建可以使用Thread.new,Thread.start 或者Thread.fork来创建线程。例如thr...

  • 线程的基本操作

    函数原型 创建线程的函数有四个参数,第一个参数是一个pthread_t类型的指针。第二个参数通常设置为NULL,第...

  • 线程的基本操作

    1.线程中断 线程中断,并不会让线程立即退出,而是给线程发送一个通知,告诉目标线程,有人希望你退出了。至于线程收到...

  • 多线程2:线程的状态转换以及基本操作

    线程的状态转换以及基本操作

  • 线程

    一、Android线程的基本介绍 在操作系统中,线程是操作系统调度的最小单元

  • 线程的状态转换以及基本操作

    线程的状态转换以及基本操作 转载

  • 实战java高并发程序设计第二章(连更)

    1.线程的基本状态2.线程的基本操作3. volatile与java内存模型4.线程组5.守护线程(Daemon)...

  • 理解协程

    一、进程、线程、协程的区别 进程:操作系统中分配资源的基本单位 线程:操作系统中调度资源的基本单位 协程:比线程更...

  • 线程 & 进程

    进程和线程都是操作系统所有的程序运行的基本单元,操作系统利用该基本单元实现操作系统对应用的并发性。 进程和线程的主...

网友评论

      本文标题:线程的基本操作

      本文链接:https://www.haomeiwen.com/subject/vdzafctx.html