多线程同步访问共享内存

下面讨论的是特定于网络编程中多线程对共享内存的同步访问,原理可以用于其它应用.

首先看看创建线程函数:

#include <pthread.h>

int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(* func) (void *), void *arg);

其中,各个参数解释如下:

  • 一个进程内的每个线程的标志为线程ID,如果创建成功,其ID通过tid指针返回.
  • 每个线程有多个属性(attribute):优先级,初始栈大小,是否为守护线程等.
  • 最后两个参数是由该线程执行的函数及其参数.函数地址由func指定,该函数的参数为指针arg.如果需要给该函数传递多个参数,那么需要将它们打包成一个结构,然后将结构的地址作为单个参数传递给func函数.

注意,我们不能简单地把一个数据的地址传递给新线程,比如说下面的代码:

01. int main(int argc, char *argv[])
02. {
03. int listenfd, connfd;
04. ...
05. for( ; ; )
06. {
07. len = addrlen;
08. connfd = accept(listenfd, cliaddr, &len);
09.  
10. pthread_create(&tid, NULL, &doit, &connfd);
11. }
12. }
13.  
14. static void * doit(void *arg)
15. {
16. int connfd;
17. connfd = *((int *) arg);
18. pthread_detach(pthread_self());
19. dosomething(connfd);
20. close(connfd);
21. return(NULL);
22. }

从ANSI C 角度看,这是可以接受的: ANSI C 保证我们能够把一个整数指针类型强制转换成void *,然后把这个(void *)指针类型强制转换回原来的整数指针类型..但是,问题就出在了这个整数指针指在什么方向上,即多线程之间对共享内存的访问并没有同步.

要解决这个问题,首先需要知道,多线程对共享内存的访问,可以分为只读,只写或者读写..

只读的话是很容易的,把connfd的值而不是指向该变量的一个指针传递给pthread_create就可以了.因为按照 C 向被调用函数传递整数值的方式,它把该值的一个拷贝推入被调用函数的栈中..

代码即改成:

01. int main(int argc, char *argv[])
02. {
03. int listenfd, connfd;
04. ...
05. for( ; ; )
06. {
07. len = addrlen;
08. connfd = accept(listenfd, cliaddr, &len);
09.  
10. pthread_create(&tid, NULL, &doit, (void *)connfd);
11. }
12. }
13.  
14. static void * doit(void *arg)
15. {
16. pthread_detach(pthread_self());
17. dosomething((int) arg);
18. close((int) arg);
19. return(NULL);
20. }

至于多线程访问共享内存的其他两种操作(只写或者读写),自然可以以对临界区加锁的方式来实现,这是众所周知的事实..至少,面试官gg是这么想的,先是问了下多线程同步访问共享内存,见我说用信号量,赶紧加了个不加锁的条件,我真成杯具批发商了..

只不过,其实还有一个很好的方法的,利用malloc和free这两个函数不可重入的特性!

我们可以这么做: 每当调用accept时,先用malloc分配一个整数变量的内存空间,用于存储有待accept返回的已连接描述字.这就使得每个线程都有各自的已连接描述字拷贝.在线程处理函数中,获取已连接描述字的值后,调用free函数以释放内存空间.因为malloc和free函数的不可重入,POSIX将其规定为线程安全的(thread_safe).遵守POSIX的各大Unix产商的malloc和free函数自然也就是线程安全的了,否则,在主程序正处于这两个函数之一的内部处理期间,从某个信号处理函数中调用这两个函数之一将可能导致灾难性的后果,这是因为这两个函数操纵着相同的静态数据结构.

最后,使用malloc和free函数的代码如下:

01. int main(int argc, char *argv[])
02. {
03. int listenfd, *iptr;
04. ...
05. for( ; ; )
06. {
07. len = addrlen;
08. iptr = malloc(sizeof(int));
09. *iptr = accept(listenfd, cliaddr, &len);
10.  
11. pthread_create(&tid, NULL, &doit, iptr);
12. }
13. }
14.  
15. static void * doit(void *arg)
16. {
17. int connfd;
18. connfd = *((int *) arg);
19. free(arg);
20. pthread_detach(pthread_self());
21. dosomething(connfd);
22. close(connfd);
23. return(NULL);
24. }
点评:

拿 malloc – free 来替代信号灯,理论上可以。

事实上,这真是个蹩脚的想法。malloc-free的处理,是要有内存链表查表过程的,有一定开销。

好的程序,是不会频繁malloc-free的,一般预准备份内存区,供多个线程读。

信号灯这一族函数是专门处理内在逻辑关系的,各方面优于malloc-free,舍其不用吗?


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值