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 \n", id); while(1) { pthread_mutex_lock(p_mutex); if(ticket_cnt == 0) { printf("ticket out! \n"); 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 \n", 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...\n"); 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\n",val); pthread_exit((void*)"hello world"); } int main() { pthread_t thd; int a=12345; pthread_create(&thd,NULL,handler,(void*)&a); printf("thd: %x\n",thd); pthread_join(thd,(void*)&ret); return 0; }
方式2:
void* handler(void *arg) { int val=(int)arg; printf("from main: %d\n",val); pthread_exit((void*)"hello world"); } int main() { pthread_t thd; int a=12345; pthread_create(&thd,NULL,handler,(void*)a); printf("thd: %x\n",thd); pthread_join(thd,(void*)&ret); return 0; }
对于方式1,传给handler的参数是a的地址,如果a的值之后在主线程里会发生变化,那么传给handler的数值a可能就不是原来我们想传的那个了。
而对于方式2,由于是值传递,我们当时想传什么,传到handler中的就一定是什么。
由此可见,虽然方式1节省了存储空间,但是同样也容易发生错误。