信号量线程控制

1.线程定义
线程是进程内独立的一条运行路线,可以称为轻量级进程,与同一进程内的其他线程共享内存空间及资源。因此,线程的上下文切换的开销比创建进程小很多。
一个进程可以有多个线程,由于线程共享进程的内存空间和资源,多线程中的同步是非常重要的问题。
2.线程间的同步与互斥
针对线程共享进程内存空间及资源的问题,POSIX中提供了相应的同步机制,如互斥锁和信号量。这两个同步机制可以互相通过调用对方来实现,但互斥锁更适合用于同时可用的资源是惟一的情况;信号量更适合用于同时可用的资源为多个的情况。下面主要介绍信号量。
3.信号量
信号量就是操作系统中所用到的PV原子操作,它广泛用于进程或线程间的同步与互斥。信号量本质上是一个非负的整数计数器。
3.1 PV原子操作的工作原理

PV 原子操作是对整数计数器信号量 sem 的操作。一次 P 操作使 sem 减一,而一次 V 操作使 sem 加一。
进程(或线程)根据信号量的值来判断是否对公共资源具有访问权限。当信号量 sem 的值大于等于零
时,该进程(或线程)具有公共资源的访问权限;相反,当信号量 sem 的值小于零时,该进程(或线
程)就将阻塞直到信号量 sem 的值大于等于 0 为止。
PV 原子操作主要用于进程或线程间的同步和互斥这两种典型情况。若用于互斥,几个进程(或线程)往
往只设置一个信号量 sem,它们的操作流程如图 3.1所示。
当信号量用于同步操作时,往往会设置多个信号量,并安排不同的初始值来实现它们之间的顺序执行,它
们的操作流程如图 3.2 所示。
这里写图片描述

3.2 函数说明
Linux 实现了POSIX的无名信号量,主要用于线程间的互斥与同步。

– sem_init() 创建一个信号量,并初始化它的值。
这里写图片描述

–sem_wait() 和 sem_trywait()都相当于P操作,在信号量大于零时他们都能讲信号量的值减一,区别在于若信号量小于零时,sem_wait()将会阻塞进程,而sem_trywait()则会立即返回。
sem_post()相当于V操作,它将信号量的值加一同时发出信号来唤醒等待的线程。
sem_getvalue()用于得到信号量的值。
sem_destroy()用于删除信号量。
这里写图片描述

3.3 实例

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>

#define THREAD_NUMBER  3              //线程数
#define REPEAT_NUMBER  3              //每个小城的小任务数
#define DELAY_TIME_LEVELS 10.0        //小任务之间的最大时间间隔

/*RAND_MAX定义在stdlib.h,其值为2147483647*/

sem_t sem[THREAD_NUMBER];

void *thrd_func(void *arg)
{
    int thrd_num = (int)arg;
    int delay_time = 0;
    int count = 0;

    /*进行p操作*/
    sem_wait(&sem[thrd_num]);

    printf("Thread %d is starting\n",thrd_num);

    for(cont=0;count<REPEAT_NUMBER;count++)
    {
        delay_time = (int)(rand()*DELAY_TIME_LEVELS/RAND_MAX)+1;
        sleep(delay_time);
        printf("\tThread %d: job %d delay = %d\n",thrd_num, count, delay_time);
    }

    printf("Thread %d finished\n",thrd_num);

    pthread_exit(NULL);
}

int main(void)
{
    pthread_t thread[THREAD_NUMBER];
    int no=0,res;
    void * thrd_ret;

    /********    srand(unsigned int seed)     用来设置rand()产生随机数时的随机数种子*********/
    /** 参数seed必须是个整数,通常可以利用geypid()或time(0)的返回值来当做seed **/
    srand(time(NULL));

    for(no=0;no<THREAD_NUMBER;no++)
    {
        sem_init(&sem[no],0,0);//信号量设为 0
        res = pthread_create(&thread[no],NULL,thrd_func,(void *)no);
        if(res !=0)
        {
            printf("Create thread %d failed\n",no);
            exit(res);
        }
    }

    printf("Create treads success\n Waiting for threads to finish...\n");

    /* 对最后创建的线程的信号量进行V 操作*/
    sem_post(&sem[THREAD_NUMBER-1]);
    for(no=THREAD_NUMBER-1;no>=0;no--)
    {
        res=pthread_join(thread[no],&thrd_ret);//等待线程结束
        if(!res)
        {
            printf("Thread %d joined\n",no);
        }
        else
        {
            printf("Thread %d join failed\n", no);
        }

        /*进行 V 操作*/
        sem_post(&sem[(no+THREAD_NUMBER-1)%THREAD_NUMBER]);
    }

    for(no=0;no<THREAD_NUMBER;no++)
    {
        /*删除信号量*/
        sem_destroy(&sem[no]);
    }

    return 0;
}

编译后下载到开发板运行,
这里写图片描述

4. 多线程程序的编译问题

采用线程执行的程序编译时,如上面的程序,用一般的编译选项 arm-linux-gcc -o threadsem thread_sem.c,出现编译错误:
/tmp/ccngl594.o: In function thrd_func':
thread_sem.c:(.text+0x3c): undefined reference to
sem_wait’
/tmp/ccngl594.o: In function main':
thread_sem.c:(.text+0x180): undefined reference to
sem_init’
thread_sem.c:(.text+0x1a4): undefined reference to pthread_create'
thread_sem.c:(.text+0x1f4): undefined reference to
sem_post’
thread_sem.c:(.text+0x22c): undefined reference to pthread_join'
thread_sem.c:(.text+0x2a0): undefined reference to
sem_post’
thread_sem.c:(.text+0x2dc): undefined reference to `sem_destroy’
collect2: ld returned 1 exit status

这个是因为pthread并非Linux系统的默认库,编译时注意加上-lpthread参数,以调用链接库
我们再一次在终端输入:arm-linux-gcc -o threadsem thread_sem.c -lpthread,编译成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值