zoukankan      html  css  js  c++  java
  • Linux多线程之互斥

    题目

    共要卖票20张,由命令行输入窗口数,由线程模拟窗口。每卖掉一张票,屏幕显示由几号窗口所卖,一并显示剩余票数

    思路

    由于票数 ticket_cnt 是全局变量,因此每当一个线程将其减一(卖出一张票),并将其显示,应该被封装为一个原子操作。因为线程是并发执行的,可能当前线程将ticket_cnt减1后还没有来得及显示此时的剩余票数ticket_cnt,ticket_cnt已经被另一个线程减一了。此处通过互斥锁实现互斥。

    函数原型

    创建线程:

    NAME
           pthread_create - create a new thread
    
    SYNOPSIS
           #include <pthread.h>
    
           int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                              void *(*start_routine) (void *), void *arg);
    
           Compile and link with -pthread.

    回收线程资源,该函数为阻塞函数。

    NAME
           pthread_join - join with a terminated thread
    
    SYNOPSIS
           #include <pthread.h>
    
           int pthread_join(pthread_t thread, void **retval);
    
           Compile and link with -pthread.
    
    DESCRIPTION
           The  pthread_join() function waits for the thread specified
           by thread to terminate.  If that thread has already  termi-
           nated, then pthread_join() returns immediately.  The thread
           specified by thread must be joinable.
    
           If retval is not NULL, then pthread_join() copies the  exit
           status  of the target thread (i.e., the value that the tar-
           get thread supplied to pthread_exit(3)) into  the  location
           pointed  to by *retval.  If the target thread was canceled,
           then PTHREAD_CANCELED is placed in *retval.
    
           If multiple threads simultaneously try  to  join  with  the
           same  thread,  the  results  are  undefined.  If the thread
           calling pthread_join() is canceled, then the target  thread
           will remain joinable (i.e., it will not be detached).
    
    RETURN VALUE
           On  success, pthread_join() returns 0; on error, it returns
           an error number.

    解锁开锁

    NAME
           pthread_mutex_lock
           pthread_mutex_unlock - lock and unlock a mutex
    
    SYNOPSIS
           #include <pthread.h>
    
           int pthread_mutex_lock(pthread_mutex_t *mutex);
           int pthread_mutex_unlock(pthread_mutex_t *mutex);

    初始化锁,销毁锁

    NAME
           pthread_mutex_destroy,  pthread_mutex_init  -  destroy  and initialize a mutex
    
    SYNOPSIS
           #include <pthread.h>
    
           int pthread_mutex_destroy(pthread_mutex_t *mutex);
           int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

    代码

    /*************************************************************************
      > File Name: ticket.c
      > Author: KrisChou
      > Mail:zhoujx0219@163.com 
      > Created Time: Mon 25 Aug 2014 07:40:38 PM CST
     ************************************************************************/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <pthread.h>
    
    int ticket_cnt = 20;     /* 共有20张票 */
    typedef struct tag
    {
        int s_id;
        pthread_mutex_t *s_p;
    }DATA,*pDATA;
    
    void* handler(void *arg )
    {
        int id = ((pDATA)arg)->s_id;
        pthread_mutex_t *p_mutex = ((pDATA)arg)-> s_p;
        printf("a window on !: %d 
    ", id);
        while(1)
        {
            pthread_mutex_lock(p_mutex);
            if(ticket_cnt == 0)
            {
                printf("ticket out! 
    ");
                pthread_mutex_unlock(p_mutex);
                free((pDATA)arg);
                return (void*)0;
            }
            --ticket_cnt;
            sleep(rand()%3 + 1);
            printf("window: %d : a ticket sold. left : %d 
    ", id,ticket_cnt );
            pthread_mutex_unlock(p_mutex);
            sleep(rand() % 3 + 1); /* 如果不sleep,锁会一直被这个执行完的线程所占据 */
    
        }
    }
    
    int main(int argc, char *argv[])
    {
        srand(getpid());
        pthread_mutex_t mutex;
        pthread_mutex_init(&mutex, NULL);
        int thd_cnt = atoi(argv[1]); /* 从命令行输入卖票窗口数 */
        pthread_t *tds = (pthread_t*)calloc(thd_cnt,sizeof(pthread_t));
        int index;
        for(index = 0; index < thd_cnt; index++ )
        {
            pDATA p = (pDATA)calloc(1,sizeof(DATA));
            p->s_id = index;
            p->s_p = &mutex;
            pthread_create(tds + index , NULL,handler,(void*)p);
        }
        printf("joining...
    ");
        for(index = 0; index < thd_cnt; index++)
        {
            pthread_join(tds[index],NULL);
        }
        pthread_mutex_destroy(&mutex);
        return 0;
    }

    编译运行:

    [purple@localhost review]$ gcc ticket.c -lpthread
    [purple@localhost review]$ ./a.out 5
    joining...
    a window on !: 2
    a window on !: 3
    a window on !: 4
    a window on !: 1
    a window on !: 0
    window: 2 : a ticket sold. left : 19
    window: 3 : a ticket sold. left : 18
    window: 4 : a ticket sold. left : 17
    window: 1 : a ticket sold. left : 16
    window: 0 : a ticket sold. left : 15
    window: 2 : a ticket sold. left : 14
    window: 3 : a ticket sold. left : 13
    window: 4 : a ticket sold. left : 12
    window: 1 : a ticket sold. left : 11
    window: 0 : a ticket sold. left : 10
    window: 2 : a ticket sold. left : 9
    window: 3 : a ticket sold. left : 8
    window: 4 : a ticket sold. left : 7
    window: 1 : a ticket sold. left : 6
    window: 0 : a ticket sold. left : 5
    window: 2 : a ticket sold. left : 4
    window: 3 : a ticket sold. left : 3
    window: 4 : a ticket sold. left : 2
    window: 1 : a ticket sold. left : 1
    window: 0 : a ticket sold. left : 0
    ticket out!
    ticket out!
    ticket out!
    ticket out!
    ticket out!

    干货

    如果有一个整型参数 a 需要在创建线程时将其传递给线程,那么以下两种传值方式,实际上是由区别的。来看代码:

    方式1:

    void* handler(void *arg)
    {
        int val=*(int*)arg;
        printf("from main: %d
    ",val);
        pthread_exit((void*)"hello world");
    }
    int main()
    {
        pthread_t thd;
        int a=12345;
        pthread_create(&thd,NULL,handler,(void*)&a);
        printf("thd: %x
    ",thd);
        pthread_join(thd,(void*)&ret);
        return 0;
    }

    方式2:

    void* handler(void *arg)
    {
        int val=(int)arg;
        printf("from main: %d
    ",val);
        pthread_exit((void*)"hello world");
    }
    int main()
    {
        pthread_t thd;
        int a=12345;
        pthread_create(&thd,NULL,handler,(void*)a);
        printf("thd: %x
    ",thd);
        pthread_join(thd,(void*)&ret);
        return 0;
    }

    对于方式1,传给handler的参数是a的地址,如果a的值之后在主线程里会发生变化,那么传给handler的数值a可能就不是原来我们想传的那个了。

    而对于方式2,由于是值传递,我们当时想传什么,传到handler中的就一定是什么。

    由此可见,虽然方式1节省了存储空间,但是同样也容易发生错误。

  • 相关阅读:
    【其他】parallels Desktop解决无法压缩硬盘的问题
    【读书笔记】《互联网企业安全高级指南》1-5
    【漏洞复现】SMBLoris windows拒绝服务漏洞
    【编程开发】macOS下配置scapy环境
    C#异常处理经验(原则与方法)
    EntityFramework linq 多条件查询,不定条件查询
    C#制作、打包、签名、发布Activex全过程
    ZPL语言完成条形码的打印
    Linux环境下MySql安装和常见问题的解决
    lrzsz——一款好用的文件互传工具
  • 原文地址:https://www.cnblogs.com/jianxinzhou/p/3936012.html
Copyright © 2011-2022 走看看