Linux系统下的线程控制

    想要了解线程的控制,首先我们要理解线程的概念。

    线程的概念:

    进程是操作系统中资源管理的最小单位。线程是程序执行的最小单位。
    在操作系统设计上,从进程演化出线程最主要的目的就是更好地支持多处理器以及减少上下文切换开销。
    一个进程至少需要一个线程作为它的指令执行体,进程管理着计算机资源,而将线程分配到某个 CPU 上执行。
    对操作系统来说,进程占有系统资源,进程的切换也给操作系统带来了额外的开销。每次创建新进程会把父进程的资源复制一份到子进程,如果创建多个进程的话,会占用大量的资源。
    进程间的数据共享也需要操作系统的干预。
    线程是一种轻量级的进程。
    线程没有系统资源。
    线程是操作系统调度的最小单位,一个进程由一个或多个线程组成。
    在操作系统内核中,是按照线程作为调度单位来调度资源的。
    在一个进程内部,多个线程之间的资源是共享的。
    目前Linux中最流行的线程机制为LinuxThreads,采用的是基于核心轻量级进程的“一对一”模型。一个线程对应一个核心轻量级进程,进程调度由Linux内核完成,而线程的管理在核外函数库中实现。


    进程与线程的对比:

    进程和线程有许多相似之处,但是也有许多不同:

   (1).资源分配不同

    进程拥有独立的内存和系统资源,而在一个进程内部,线程之间的资源是共享的,系统不会为线程分配系统资源。

   (2). 工作效率不同

    进程拥有系统资源,在系统切换的时候,操作系统要保留进程占用的资源;线程的切换不需要保留系统资源,切换效率远高于进程。

   (3).执行方式不同

     线程有程序运行的入口地址,但是线程不能独立运行。由于线程不占有系统资源,所以线程必须放在进程中。进程可以被操作系统直接调度。一个进程内部的多个线程可共享资源和调度,不同进程之间的线程资源不能直接共享。


    系统调用线程控制的过程:

    ( 1) .线程创建
    格式:

    #include <pthread.h>

    int pthread_create(pthread_t *thread, constpthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

    说明: pthread_create ()为调用的进程创建一个新线程。其中参数 thread 为线程标识符、 attr 为线程属性设置、 start_routine 为线程函数起始地址、 arg 为传递给 start_routine 的参数。
    创建线程成功时,返回 0 ,创建线程失败,返回错误号。 pthread_create 是通过系统调用 clone 来实现的, clone Linux 特有的系统调用,类似进程创建的系统调用 fork ()。

   
    ( 2) .获得线程标识符
    格式:

    #include <pthread.h>

    pthread_tpthread_self(void);

    说明: pthread_t pthread_self ()返回调用的线程的标识符。每个线程都有自己的线程标识符,以便在进程内区分,线程标识符在 pthread_create 创建时产生。

    ( 3) .线程等待
    格式:

    #include <pthread.h>

    int pthread_join(pthread_t thread, void **retval);

    说明: pthread_join ()将调用它的线程阻塞,一直等到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。第一个参数 thread 为被等待的线程标识符,第二个参数 retval 为用户定义的指针,存放被等待线程的返回值。

    (4) .线程退出
    格式:

    #include <pthread.h>

    void pthread_exit(void *retval);

    int pthread_cancel(pthread_t thread);

    说明: pthread_exit ()终止调用线程, retval 为线程的返回值; pthread_cancel 终止由参数 thread 指定的线程。

    程序要求:
    编写程序创建一个线程,该线程显示 3 次字符串“ This is a pthread ,父进程显示 3 次字符串“ This is the main process”

    程序实例:
#include <stdio.h>
#include <pthread.h>
void thread(void)
{
    int i;
    for (i=0;i<3;i++)
       printf("This is a pthread.\n");
}

int main(void)
{
    pthread_t id;
    int i,ret;
    ret=pthread_create(&id,NULL,(void *) thread,NULL);
    if (ret!=0)
    {
        printf ("Create pthread error!\n");
        exit (1);
    }
    for (i=0;i<3;i++)
        printf("This is the main process.\n");
    pthread_join(id,NULL);
    return(0);
}

    编译并运行,因线程相关函数是运行在用户空间的线程库pthread.h实现,所以编译的时候要加上-lpthread选项



    线程同步:
   
    多线程编程因为无法知道哪个线程会在哪个时候对共享资源进行操作,因此让如何保护共享资源变得复杂,通过 使用线程间的同步, 可以解决线程间对资源的竞争
    包括两种基本方法,第一种是“信号量”,第二种是“互斥量”。选择哪种方法取决于程序的实际需要。例如控制共享内存,使之在任何一个时刻只有一个线程能够对它进行访问,使用互斥量更为合适。但如果需要控制一组同等对象的访问权,例如从 5 条电话线里给某个线程分配一条,计数信号量就更合适。

    1.信号量同步

    信号量相关的函数名字以“ sem 作为前缀,线程里使用的基本信号量函数有 4 个,被包含在头文件 semaphore.h 中。初始化信号量可使用函数 sem_init () ,它的一般形式是:

      intsem_init(sem_t*sem,intpshared, unsignedint value);

     其中,第一个参数是sem_t结构的指针,该结构用于保存信号量的信息。第二个参数控制信号量的类型,如果参数值为0,表示该信号量是局部的,否则其它程序就能共享这个信号量。第三个参数是信号量的初始值。


    2.互斥量同步
    互斥量的作用犹如给某个对象加上一把锁,每次只允许一个线程去访问它。如果想对代码关键部分的访问进行控制,可以在进入这段代码之前锁定一个互斥量,完成操作之后再解开它。
    使用互斥量要用到的基本函数与信号量需要使用的函数很相识,同样是 4 个,它们的一般形式如下:

    int pthread_mutex_init(pthread_mutex_t*mutex, const pthread_mutexattr_t*mutexattr);

    int pthread_mutex_lock(pthread_mutex_t*mutex));

    int pthread_mutex_unlock(pthread_mutex_t*mutex);

    int pthread_mutex_destroy(pthread_mutex_t*mutex);

    pthread_mutex_init( )函数用于创建一个互斥量,第一个参数是指向互斥量的数据结构pthread_mutex_t的指针,第二个参数是定义互斥量属性的pthread_mutexattrt结构的指针,它的默认类型是fast。类似于信号量的使用方法。

    pthread_mutex_lock( )是对互斥量进行锁定操作,pthread_mutex_unlock()是对互斥量进行解锁操作。函数pthread_mutex_destroy()的作用是清除互斥量。

   
    对一个已经加了锁的互斥量调用 pthread_mutex_lock () 函数,那么程序本身就会被阻塞;而因为拥有互斥量的那个线程现在也是被阻塞的线程之一,所以互斥量就永远也打不开了,程序将进入死锁状态。

    要避免死锁有两种做法:一是让它检测有可能发生死锁的这种现象并返回一个错误;二是让它递归地操作,允许同一个线程加上好几把锁,但前提是以后必须有同等数量的解锁钥匙。程序如下:
 
    3.两种方法对比
    Mutex 是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。
    Semaphore 是一件可以容纳 N 人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。对于 N=1 的情况,称为 binary semaphore

    Binary semaphore Mutex 的差异:

   1. mutex要由获得锁的线程来释放(谁获得,谁释放)。而semaphore可以由其它线程释放

   2.初始状态可能不一样:mutex的初始值是1 ,而semaphore的初始值可能是0(或者为1)。



    取消线程:

    有时需要让一个线程能请求另外一个线程结束,可使用 pthread_cancel () 函数发送一个要求取消线程的信号。该函数的一般形式是:

     intpthread_cancel(pthread_tthread);

     参数中指定的线程在收到取消请求后,会对自己稍做一些处理,然后结束。

    在线程函数中可使用pthread_setcancelstate()设置自己的取消状态,该函数的一般形式是:

    int pthread_setcancelstate ( int state, int * oldstate );

    第一个参数是状态的设定值,它可以是一个枚举常量,定义有: PTHREAD_CANCEL_ENABLE ,这个值允许线程接收取消请求; PTHREAD_CANCEL_DISABLE ,这个值屏蔽取消请求。第二个参数是线程的取消状态,该状态的定义与创建线程的函数相同,如果没有特殊要求可传送 NULL
 
    如果取消请求被接受了,线程会进入第二个控制层次,用 pthread_setcanceltype () 函数设置取消类型。函数的一般形式是:

    intpthread_setcanceltype(int type,int*oldtype);

    type 参数可以有两种取值,一个是 PTHREAD_CANCEL_ASYNCHRONOUS ,表示线程接受取消请求后立即采取行动;另一个是 PTHREAD_CANCEL_DEFERRED ,表示在接收到取消请求之后、采取实际行动之前,先执行 pthread_join ()、pthread_cond_wait () pthread_cond_tomewait () pthread_testcancel () sem_wait () sigwait () 函数。程序实现如下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>    //包含线程库
#include<string.h>

void *thread_function(void *arg);    //定义线程函数原型
char message[]= "THREAD_TEST";      //定义公用的内存空间

int main()
{
    int res;                           //用于保存线程的返回值
    pthread_t a_thread;                //用于保存线程的标识符等信息
    void *thread_result;               //用于接受线程结束时的返回值

    res=pthread_create(&a_thread,NULL,thread_function,(void*)message);   //创建线程

    if(res!=0)                          //判断线程是否有错误
    {
        perror("线程创建失败");
        exit(EXIT_FAILURE);
    }

    printf("等待线程结束...\n");
    res=pthread_join(a_thread, &thread_result);                     //等待线程结束

    if(res!=0)                                                   //判断线程是否有错误
    {
        perror("等待线程结束");
        exit(EXIT_FAILURE);
    }

    printf("线程已结束,返回值:%s\n",(char *)thread_result);    //输出线程返回的消息

    printf("Message的值为:%s\n",message);                       //输出公用的内存空间的值
    exit(EXIT_SUCCESS);
    
}

void *thread_function(void *arg)                       //定义线程函数的细节
{
    printf("线程在运行,参数为:%s\n",(char *)arg);   //输出线程的参数
    sleep(3);                                         //使线程休眠3秒
    strcpy(message,"线程修改");                          //修改公用的内存空间的值
    pthread_exit("线程执行完毕");                     //结束线程
}


    程序中,原有线程在睡眠 3 秒后,发出一个结束新线程的请求。新线程的取消状态被设置为允许取消,取消的类型为延迟取消。当新线程收到取消请求后,至少执行了 pthread_join () 函数。这样原有线程里就能收到新线程已经取消的消息了。


    多线程的实现:
   
    程序运行时创建的线程可以被当作主线程,主线程可以创建出多个线程,而新创建的线程里也能再创建线程。程序如下:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>

#define NUM_THREADS 6    //定义线程的总数

void *thread_function(void *arg);

int main()
{
	int res;
	pthread_t a_thread[NUM_THREADS];    //定义线程数组
	void *thread_result;
	int lots_of_threads;
	for(lots_of_threads=0;lots_of_threads<NUM_THREADS;lots_of_threads++)
	{

		res=pthread_create(&(a_thread[lots_of_threads]),NULL,thread_function,(void *) &lots_of_threads);    //创建一个线程

		if(res!=0)
		{
			perror("线程创建失败");
			exit(EXIT_FAILURE);
		} 
		sleep(1);     //主线程休眠1秒
		
	}

	printf("等待线程结束...\n");

	for(lots_of_threads=NUM_THREADS-1;lots_of_threads>=0;lots_of_threads--)
	{
		res=pthread_join(a_thread[lots_of_threads],&thread_result);    //等待线程结束

		if(res==0)
		{
			printf("结束一个线程\n");
		}
		else
		{
			perror("线程结束失败");	
		}
	}

	printf("线程结束失败\n");
	exit(EXIT_SUCCESS);
}


void *thread_function(void *arg)       //定义线程函数
{
	int my_number=*(int *)arg;      //接受主线程传递的参数,该参数可以是任意类型
	int rand_num;
	printf("线程函数已运行,参数为:%d\n",my_number);
	rand_num=1+(int)(9.0*rand()/(RAND_MAX+1.0));      //获得一个随机数
	sleep(rand_num);                                 //线程以随机数定义的时间休眠
	printf("第%d个线程结束\n",my_number);            //结束线程
	pthread_exit(NULL);
	
}



  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

daijingxin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值