在Quartez中操作任务类设置阻塞时间,让下个线程延迟,用sleep(5000)。但是下一个线程还是按时触发了

在Quartez中操作任务类设置阻塞时间,让下个线程延迟,用sleep(5000)。但是下一个线程还是按时触发了,第1张

援引CU上一篇帖子的内容:

信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里)。而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这 个资源。比如对全局变量的访问,有时要加锁,操作完了,在解锁。有的时候锁和信号量会同时使用的”

也就是说,信号量不一定是锁定某一个资源,而是流程上的概念,比如:有A,B两个线程,B线程要等A线程完成某一任务以后再进行自己下面的步骤,这个任务 并不一定是锁定某一资源,还可以是进行一些计算或者数据处理之类。而线程互斥量则是“锁住某一资源”的概念,在锁定期间内,其他线程无法对被保护的数据进 行操作。在有些情况下两者可以互换。

两者之间的区别:

作用域

信号量: 进程间或线程间(linux仅线程间)

互斥锁: 线程间

上锁时

信号量: 只要信号量的value大于0,其他线程就可以sem_wait成功,成功后信号量的value减一。若value值不大于0,则sem_wait阻塞,直到sem_post释放后value值加一

互斥锁: 只要被锁住,其他任何线程都不可以访问被保护的资源

成功后否则就阻塞

以下是信号灯(量)的一些概念:

信号灯与互斥锁和条件变量的主要不同在于”灯”的概念,灯亮则意味着资源可用,灯灭则意味着不可用。如果说后两中同步方式侧重于”等待”操作,即资 源不可用的话,信号灯机制则侧重于点灯,即告知资源可用;没有等待线程的解锁或激发条件都是没有意义的,而没有等待灯亮的线程的点灯操作则有效,且能保持 灯亮状态。当然,这样的操作原语也意味着更多的开销。

信号灯的应用除了灯亮/灯灭这种二元灯以外,也可以采用大于1的灯数,以表示资源数大于1,这时可以称之为多元灯。

1. 创建和 注销

POSIX信号灯标准定义了有名信号灯和无名信号灯两种,但LinuxThreads的实现仅有无名灯,同时有名灯除了总是可用于多进程之间以外,在使用上与无名灯并没有很大的区别,因此下面仅就无名灯进行讨论。

int sem_init(sem_t *sem, int pshared, unsigned int value)

这是创建信号灯的API,其中value为信号灯的初值,pshared表示是否为多进程共享而不仅仅是用于一个进程。LinuxThreads没有实现 多进程共享信号灯,因此所有非0值的pshared输入都将使sem_init()返回-1,且置errno为ENOSYS。初始化好的信号灯由sem变 量表征,用于以下点灯、灭灯操作。

int sem_destroy(sem_t * sem)

被注销的信号灯sem要求已没有线程在等待该信号灯,否则返回-1,且置errno为EBUSY。除此之外,LinuxThreads的信号灯 注销函数不做其他动作。

2. 点灯和灭灯

int sem_post(sem_t * sem)

点灯操作将信号灯值原子地加1,表示增加一个可访问的资源。

int sem_wait(sem_t * sem)

int sem_trywait(sem_t * sem)

sem_wait()为等待灯亮操作,等待灯亮(信号灯值大于0),然后将信号灯原子地减1,并返回。sem_trywait()为sem_wait()的非阻塞版,如果信号灯计数大于0,则原子地减1并返回0,否则立即返回-1,errno置为EAGAIN。

3. 获取灯值

int sem_getvalue(sem_t * sem, int * sval)

读取sem中的灯计数,存于*sval中,并返回0。

4. 其他

sem_wait()被实现为取消点,而且在支持原子”比较且交换”指令的体系结构上,sem_post()是唯一能用于异步信号处理函数的POSIX异步信号 安全的API。

----------------------------

线程同步:何时互斥锁不够,还需要条件变量?

假设有共享的资源sum,与之相关联的mutex 是lock_s.假设每个线程对sum的操作很简单的,与sum的状态无关,比如只是sum++.那么只用mutex足够了.程序员只要确保每个线程操作 前,取得lock,然后sum++,再unlock即可.每个线程的代码将像这样

add()

{

pthread_mutex_lock(lock_s)

sum++

pthread_mutex_unlock(lock_s)

}

如果操作比较复杂,假设线程t0,t1,t2的操作是sum++,而线程t3则是在sum到达100的时候,打印出一条信息,并对sum清零. 这种情况下,如果只用mutex, 则t3需要一个循环,每个循环里先取得lock_s,然后检查sum的状态,如果sum>=100,则打印并清零,然后unlock.如果sum&lt100,则unlock,并sleep()本线程合适的一段时间.

这个时候,t0,t1,t2的代码不变,t3的代码如下

print()

{

while (1)

{

pthread_mutex_lock(lock_s)

if(sum<100)

{

printf(“sum reach 100!”)

pthread_mutex_unlock(lock_s)

}

else

{

pthread_mutex_unlock(lock_s)

my_thread_sleep(100)

return OK

}

}

}

这种办法有两个问题

1) sum在大多数情况下不会到达100,那么对t3的代码来说,大多数情况下,走的是else分支,只是lock和unlock,然后sleep().这浪费了CPU处理时间.

2) 为了节省CPU处理时间,t3会在探测到sum没到达100的时候sleep()一段时间.这样却又带来另外一个问题,亦即t3响应速度下降.可能在sum到达200的时候,t4才会醒过来.

3) 这样,程序员在设置sleep()时间的时候陷入两难境地,设置得太短了节省不了资源,太长了又降低响应速度.真是难办啊!

这个时候,condition variable内裤外穿,从天而降,拯救了焦头烂额的你.

你首先定义一个condition variable.

pthread_cond_t cond_sum_ready=PTHREAD_COND_INITIALIZER

t0,t1,t2的代码只要后面加两行,像这样

add()

{

pthread_mutex_lock(lock_s)

sum++

pthread_mutex_unlock(lock_s)

if(sum>=100)

pthread_cond_signal(&cond_sum_ready)

}

而t3的代码则是

print

{

pthread_mutex_lock(lock_s)

while(sum<100)

pthread_cond_wait(&cond_sum_ready, &lock_s)

printf(“sum is over 100!”)

sum=0

pthread_mutex_unlock(lock_s)

return OK

}

注意两点:

1) 在thread_cond_wait()之前,必须先lock相关联的mutex, 因为假如目标条件未满足,pthread_cond_wait()实际上会unlock该mutex, 然后block,在目标条件满足后再重新lock该mutex, 然后返回.

2) 为什么是while(sum<100),而不是if(sum<100) ?这是因为在pthread_cond_signal()和pthread_cond_wait()返回之间,有时间差,假设在这个时间差内,还有另外一 个线程t4又把sum减少到100以下了,那么t3在pthread_cond_wait()返回之后,显然应该再检查一遍sum的大小.这就是用 while的用意

/*基于信号量采用多线程技术实现进程同步*/

#include <pthread.h>

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <semaphore.h>

#include <sys/time.h>

#include <math.h>

#define CHAIRS 5 //椅子数

sem_t customers //等待服务的顾客信号量

sem_t barbers  //等待顾客的理发师信号量

pthread_mutex_t mutex //互斥变量

int waiting = 0 //正在等待的顾客数

void *barber(void *arg)

void *customer(void *num)

void cut_hair(void)

double timediff(struct timeval i,struct timeval j)

void seed_random(void)

double flat(void)

double normal(void)

double bursty(void)

int main()

{

   int i

   seed_random()

   pthread_t barber_t,customer_t

   int error

   error=pthread_create(&barber_t,NULL,barber,NULL)//创建理发师线程

   if(error!=0) {

      printf("pthread_create is not created.../n")

      return -1

   }

   while(1) {

      usleep(30000)//等待时间如果小于理发师理发时间则会出现等待者过多,否则不会出现等待者过多的现象

      error=pthread_create(&customer_t,NULL,customer,NULL)//创建顾客线程

      if(error!=0) {

         printf("pthread_create is not created.../n")

         return -1

      }

   }

}

double timediff(struct timeval now,struct timeval earlier)

{

   if(now.tv_sec == earlier.tv_sec)

      return (now.tv_usec - earlier.tv_usec)/1000000.0

   else

      return (1000000*(now.tv_sec - earlier.tv_sec) + now.tv_usec - earlier.tv_usec)/1000000.0

}

void *barber(void *arg)

{

   while(1)

   {

      sem_wait(&customers)//顾客信号量-1

      pthread_mutex_lock(&mutex)

      waiting = waiting -1

      sem_post(&barbers)//

      pthread_mutex_unlock(&mutex)

      cut_hair()//理发

   }

}

void cut_hair(void)

{

   printf("  Barber:I am cutting the customer's hair.../n")

   usleep(100000)//理发时间

   printf("  Barber:done./n")

}

void *customer(void *num)

{

   pthread_mutex_lock(&mutex)

   if(waiting<CHAIRS)

   {

       waiting = waiting + 1

       sem_post(&customers)

       pthread_mutex_unlock(&mutex)

       sem_wait(&barbers) 

   }

   else

   {

      printf("  Waiter is too much.../n")

      pthread_mutex_unlock(&mutex)

   }

   //释放占用的资源

}

void seed_random(void)

{

   struct timeval randtime

   unsigned short xsub1[3]

   gettimeofday(&randtime,(struct timezone *)0)

   xsub1[0] = (ushort)randtime.tv_usec

   xsub1[1] = (ushort)(randtime.tv_usec >> 16)

   xsub1[2] = (ushort)(getpid())

   seed48(xsub1)

}

double flat()

{

   return drand48()/5

}

第二问 加个理发师忙碌数量 用来判断 即可

目前网上可以查找到很多关于信号量的实现文章,但是讲解在linux下使用semaphore的文章比较少;

c++ linux semaphore信号量的使用

sem_init函数是Posix信号量操作中的函数。sem_init() 初始化一个定位在 sem 的匿名信号量。value 参数指定信号量的初始值。 pshared 参数指明信号量是由进程内线程共享,还是由进程之间共享。如果 pshared 的值为 0,那么信号量将被进程内的线程共享,并且应该放置在这个进程的所有线程都可见的地址上(如全局变量,或者堆上动态分配的变量)。

如果 pshared 是非零值,那么信号量将在进程之间共享,并且应该定位共享内存区域(见 shm_open(3)、mmap(2) 和 shmget(2))。因为通过 fork(2) 创建的孩子继承其父亲的内存映射,因此它也可以见到这个信号量。所有可以访问共享内存区域的进程都可以用 sem_post(3)、sem_wait(3) 等等操作信号量。初始化一个已经初始的信号量其结果未定义。

返回值 :

sem_init() 成功时返回 0;错误时,返回 -1,并把 errno 设置为合适的值。

例子:


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/218959.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-06
下一篇2023-04-06

发表评论

登录后才能评论

评论列表(0条)

    保存