TCP/IP网络编程 多线程服务器端的实现(1)(2)
线程存在的问题和临界区
前面我们知道了怎么创建线程,但我们都是只创建了一个线程,下面我们再来看看这样一个实例,创建100个线程,它们都访问了同一变量,其中一半对这个变量进行加1操作,一半进行减1操作,按道理其结果会等于0.
- #include
- #include
- #include
- #include
- #define NUM_THREAD 100
- void * thread_inc(void * arg);
- void * thread_des(void * arg);
- long long num = 0; //long long类型是64位整数型,多线程共同访问
- int main(int argc, char *argv[])
- {
- pthread_t thread_id[NUM_THREAD];
- int i;
- //创建100个线程,一半执行thread_inc,一半执行thread_des
- for(i = 0; i < NUM_THREAD; i++)
- {
- if(i %2)
- pthread_create(&(thread_id[i]), NULL, thread_inc, NULL);
- else
- pthread_create(&(thread_id[i]), NULL, thread_des, NULL);
- }
- //等待线程返回
- for (i = 0; i < NUM_THREAD; i++)
- pthread_join(thread_id[i], NULL);
- printf("result: %lld \n", num); //+1,-1按道理结果是0
- return 0;
- }
- //线程入口函数1
- void * thread_inc(void * arg)
- {
- for (int i = 0; i < 50000000; i++)
- num += 1;//临界区(引起问题的语句就是临界区位置)
- return NULL;
- }
- //线程入口函数2
- void * thread_des(void * arg)
- {
- for (int i = 0; i < 50000000; i++)
- num -= 1;//临界区
- return NULL;
- }
从运行结果看并不是0,而且每次运行的结果都不同。那这是什么原因引起的呢? 是因为每个线程访问一个变量是这样一个过程:先从内存取出这个变量值到CPU,然后CPU计算得到改变后的值,最后再将这个改变后的值写回内存。因此,我们可以很容易看出,多个线程访问同一变量,如果某个线程还只刚从内存取出数据,还没来得及写回内存,这时其它线程又访问了这个变量,所以这个值就会不正确了。
接下来我们再来讲讲怎么解决这个问题:线程同步
线程同步
线程同步用于解决线程访问顺序引发的问题,一般是如下两种情况:
同时访问同一内存空间时发生的情况
需要指定访问同一内存空间的线程执行顺序的情况
针对这两种可能引发的情况,我们分别使用的同步技术是:互斥量和信号量。
互斥量
互斥量技术从字面也可以理解,就是临界区有线程访问,其它线程就得排队等待,它们的访问是互斥的,实现方式就是给临界区加锁与释放锁。
- #include
- int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); //创建互斥量
- int pthread_mutex_destroy(pthread_mutex_t *mutex);//销毁互斥量
- int pthread_mutex_lock(pthread_mutex_t *mutex);//加锁
- int pthread_mutex_unlock(pthread_mutex_t *mutex);//释放锁
简言之,就是利用lock和unlock函数围住临界区的两端。当某个线程调用pthread_mutex_lock进入临界区后,如果没有调用pthread_mutex_unlock释放锁退出,那么其它线程就会一直阻塞在临界区之外,我们把这种情况称之为死锁。所以临界区围住一定要lock和unlock一一对应。
实例代码:
- #include
- #include
- #include
- #include
- #define NUM_THREAD 100
- void * thread_inc(void * arg);
- void * thread_des(void * arg);
- long long num = 0;
- pthread_mutex_t mutex;
- int main(int argc, char *argv[])
- {
- pthread_t thread_id[NUM_THREAD];
- int i;
- //互斥量的创建
- pthread_mutex_init(&mutex, NULL);
- for(i = 0; i < NUM_THREAD; i++)
- {
- if(i %2)
- pthread_create(&(thread_id[i]), NULL, thread_inc, NULL);
- else
- pthread_create(&(thread_id[i]), NULL, thread_des, NULL);
- }
- for (i = 0; i < NUM_THREAD; i++)
- pthread_join(thread_id[i], NULL);
- printf("result: %lld \n", num);
- pthread_mutex_destroy(&mutex); //互斥量的销毁
- return 0;
- }
- /*扩展临界区,减少加锁,释放锁调用次数,但这样变量必须加满到50000000次后其它线程才能访问.
- 这样是延长了线程的等待时间,但缩短了加锁,释放锁函数调用的时间,这里没有定论,自己酌情考虑*/
- void * thread_inc(void * arg)
- {
- pthread_mutex_lock(&mutex); //互斥量锁住
- for (int i = 0; i < 1000000; i++)
- num += 1;
- pthread_mutex_unlock(&mutex); //互斥量释放锁
- return NULL;
- }
- /*缩短了线程等待时间,但循环创建,释放锁函数调用时间增加*/
- void * thread_des(void * arg)
- {
- for (int i = 0; i < 1000000; i++)
- {
- pthread_mutex_lock(&mutex);
- num -= 1;
- pthread_mutex_unlock(&mutex);
- }
- return NULL;
- }
评论暂时关闭