gcc no_mutex.c -onomutex -lpthread

第4章 线程间通信概述 4.1 线程之间通信的方法 4.1.1 全局变量方式 4.1.2 参数传递法 4.1.3 消息传递法 4.1.4 通过同步变量进行线程间通信 4.2 线程间同步问题概述 4.3 死锁问题 4.4 本章小结。加入gil主要的原因是为了降低程序的开发的复杂度,比如现在的你写python不需要关心内存回收的问题,因为python解释器帮你自动定期进行内存回收,你可以理解为python解释器里有一个独立的线程,每过一段时间它起wake up做一次全局轮询看看哪些内存数据是可以被清空的,此时你自己的程序 里的线程和 py解释器自己的线程是并发运行的,假设你的线程删除了一个变量,py解释器的垃圾回收线程在清空这个变量的过程中的clearing时刻,可能一个其它线程正好又重新给这个还没来及得清空的内存空间赋值了,结果就有可能新赋值的数据被删除了,为了解决类似的问题,python解释器简单粗暴的加了锁,即当一个线程运行时,其它人都不能动,这样就解决了上述的问题,这可以说是python早期版本的遗留问题。我们知道在多线程写入同一个文件的时候,会存现“线程安全”的问题(多个线程同时运行同一段代码,如果每次运行结果和单线程运行的结果是一样的,结果和预期相同,就是线程安全的)。
举个例子,当线程thrd1访问到sharedi的时候,sharedi的值是1000,然后线程thrd1将sharedi的值累加到了1001,可是线程thrd2取到sharedi的时候,sharedi的值是1000,这时候线程thrd2对sharedi的值进行加1操作,使其变成了1001,可是这个时候,sharedi的值已经被线程thrd1加到1001了,然而,thrd2并不知道,所以又将sharedi的值赋为了1001,从而导致了结果的错误。
为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地访问变量。在linux中,常用的线程同步方法有互斥量( mutex )、读写锁和条件变量,合理使用这三种方法可以保证数据的一致性,但值得的注意的是,在设计应用程序时,所有的线程都必须遵守相同的数据访问规则为前提,才能保证这些同步方法有效,如果允许某个线程在没有得到访问权限(比如锁)的情况下访问共享资源,那么其他线程在使用共享资源前都获得了锁,也会出现数据不一致的问题。总之mutex和event,从字面上看已经很明白了,一个侧重于“互斥”,即多个线程互斥地访问同一个变量、资源。
通过加锁,保证sharedi变量在进行变更的时候,只有一个线程能够取到,并在在该线程对其进行操作的时候,其它线程无法对其进行访问。

1 /************************************************************************* 2 > File Name: mutex.c 3 > Author: couldtt(fyby) 4 > Mail: fuyunbiyi@gmail.com 5 > Created Time: 2013年12月15日 星期日 17时52分24秒 6 ************************************************************************/ 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <pthread.h> 11 12 int sharedi = 0; 13 void increse_num(void); 14 15 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 16 17 int main(){ 18 int ret; 19 pthread_t thrd1, thrd2, thrd3; 20 21 ret = pthread_create(&thrd1, NULL, (void *)increse_num, NULL); 22 ret = pthread_create(&thrd2, NULL, (void *)increse_num, NULL); 23 ret = pthread_create(&thrd3, NULL, (void *)increse_num, NULL); 24 25 pthread_join(thrd1, NULL); 26 pthread_join(thrd2, NULL); 27 pthread_join(thrd3, NULL); 28 29 printf("sharedi = %d\n", sharedi); 30 31 return 0; 32 33 } 34 35 void increse_num(void) { 36 long i,tmp; 37 for(i=0; i<=100000; i++) { 38 /*加锁*/ 39 if (pthread_mutex_lock(&mutex) != 0) { 40 perror("pthread_mutex_lock"); 41 exit(EXIT_FAILURE); 42 } 43 tmp = sharedi; 44 tmp = tmp + 1; 45 sharedi = tmp; 46 /*解锁锁*/ 47 if (pthread_mutex_unlock(&mutex) != 0) { 48 perror("pthread_mutex_unlock"); 49 exit(EXIT_FAILURE); 50 } 51 } 52 }

这一次,我们的结果是正确的,锁有效得保护了我们的数据安全。然而:
锁保护的并不是我们的共享变量(或者说是共享内存),对于共享的内存而言,用户是无法直接对其保护的,因为那是物理内存,无法阻止其他程序的代码访问。事实上,锁之所以对关键区域进行了保护,在本例中,是因为所有线程都遵循了一个规则,那就是在进入关键区域钱加同一把锁,在退出关键区域钱释放同一把锁
我们从上述运行结果中可以看到,加锁是会带来额外的开销的,加锁的代码其运行速度,明显比不加锁的要慢一些,所以,在使用锁的时候,要合理,在不需要对关键区域进行保护的场景下,我们便不要画蛇添足,为其加锁了
锁有一个很明显的缺点,那就是它只有两种状态:锁定与不锁定。
信号量本质上是一个非负数的整数计数器,它也被用来控制对公共资源的访问。当公共资源增加的时候,调用信号量增加函数sem_post()对其进行增加,当公共资源减少的时候,调用函数sem_wait()来减少信号量。其实,我们是可以把锁当作一个0-1信号量的。

它们是在/usr/include/semaphore.h中进行定义的,信号量的数据结构为sem_t, 本质上,它是一个long型整数
在使用semaphore之前,我们需要先引入头文件#include <semaphore.h>
信号量减1操作,当sem=0的时候该函数会堵塞int sem_wait(sem_t *sem);
信号量加1操作int sem_post(sem_t *sem);
销毁信号量int sem_destroy(sem_t *sem);
1 /************************************************************************* 2 > File Name: sem.c 3 > Author: couldtt(fyby) 4 > Mail: fuyunbiyi@gmail.com 5 > Created Time: 2013年12月15日 星期日 19时25分08秒 6 ************************************************************************/ 7 8 #include <stdio.h> 9 #include <unistd.h> 10 #include <pthread.h> 11 #include <semaphore.h> 12 13 #define MAXSIZE 10 14 15 int stack[MAXSIZE]; 16 int size = 0; 17 sem_t sem; 18 19 // 生产者 20 void provide_data(void) { 21 int i; 22 for (i=0; i< MAXSIZE; i++) { 23 stack[i] = i; 24 sem_post(&sem); //为信号量加1 25 } 26 } 27 28 // 消费者 29 void handle_data(void) { 30 int i; 31 while((i = size++) < MAXSIZE) { 32 sem_wait(&sem); 33 printf("乘法: %d X %d = %d\n", stack[i], stack[i], stack[i]*stack[i]); 34 sleep(1); 35 } 36 } 37 38 int main(void) { 39 40 pthread_t provider, handler; 41 42 sem_init(&sem, 0, 0); //信号量初始化 43 pthread_create(&provider, NULL, (void *)handle_data, NULL); 44 pthread_create(&handler, NULL, (void *)provide_data, NULL); 45 pthread_join(provider, NULL); 46 pthread_join(handler, NULL); 47 sem_destroy(&sem); //销毁信号量 48 49 return 0; 50 }

但这个时候程序并没有崩溃,继续执行第3行,程序崩溃,提示堆错误,可以看到,如果第2行和第3行之间有很长的代码逻辑,那么也只能在第3行执行之后程序才会崩溃。先解释一下这个代码的作用:因为global.asa 文件是网站启动的文件,当一个网站被用户访问的时候,会执行application_start代码段的内容,当一个用户第一次访问时会执行session_start代码段的内容,所以此段代码的作用就是当访问的时候自动下载获取木马内容,上面遇到的就是跳转性作用的木马代码。4.以上那个例子的代码是没错的,是因为版本问题,才导致只可以使用ognl访问静态属性,而访问不了静态方法,所以我使用较低版本jar包,我把上面这个项目的一些代码复制,调整一下,改变一些名字等等,我用的是下图的版本:。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-106820-2.html
真的真的很爱你
股民二级市场接盘赔钱
笑起来很好声音也很好一句话咯加油