socket编程——多线程编程与锁

一、线程

线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
线程可以提高应用程序在多核环境下处理诸如文件I/O或者socket I/O等会产生堵塞的情况的表现性能。在Unix系统中,一个进程包含很多东西,包括可执行程序以及一大堆的诸如文件描述符地址空间等资源。在很多情况下,完成相关任务的不同代码间需要交换数据。如果采用多进程的方式,进程的创建所花的时间片要比线程大些,另外进程间的通信比较麻烦,需要在用户空间和内核空间进行频繁的切换,开销很大。但是如果使用多线程的方式,因为可以使用共享的全局变量,所以线程间的通信(数据交换)变得非常高效

二、创建线程

一个进程创建后,会首先生成一个缺省的线程,通常称这个线程为主线程(或称控制线程),主线程就是通过main函数进入的线程,由主线程调用pthread_create()创建的线程称为子线程。要注意的是:
1、子线程也可以有自己的入口函数,该函数由用户在创建的时候指定。
2、每个线程都有自己的线程ID,可以通过pthread_self()函数获取。
3、每个进程可创建的最大线程数由具体实现决定。
4、主线程和子线程的默认关系是:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有子线程执行都会终止。

pthreand_create()函数

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void
*arg);

thread:是一个pthread_t类型的指针,他用来返回该线程的线程ID。每个线程都能够通过pthread_self()来获取自己的线程ID(pthread_t类型)。

attr:第二个参数是线程的属性,其类型是pthread_attr_t类型,其定义如下:

typedef struct
{
 int detachstate; //线程的分离状态
 int schedpolicy; //线程调度策略
 struct sched_param schedparam;// 线程的调度参数
 int inheritsched; //线程的继承性
 int scope; //线程的作用域
 size_t guardsize; //线程栈末尾的警戒缓冲区大小
 int stackaddr_set;
 void * stackaddr; //线程栈的位置
 size_t stacksize; //线程栈的大小
}pthread_attr_t;

对于这些属性,我们需要设定的是线程的分离状态。如果有必要也需要修改每个线程的栈大小。每个线程创建后默认是joinable状态,该状态需要主线程调用 pthread_join 等待它退出,否则子线程在结束时,内存资源不能得到释放造成内存泄漏。所以我们创建线程时一般会将线程设置为分离状态。

pthread_attr_setstacksize()函数

#include <pthread.h>
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

pthread_create 创建线程时,若不指定分配堆栈大小,系统会分配默认值。
采用默认值可能会导致内存不足,所以要用pthread_attr_setstacksize()函数来调整设置的堆栈大小。 返回值0,-1分别表示成功与失败。

pthread_attr_setdetachstate()函数

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

pthread_attr_setdetachstate()函数主要用来设置线程属性。
主要有以下两种属性:
可会合(joinable):这种关系下,主线程需要明确执行等待操作,在子线程结束后,主线程的等待操作执行完毕,子线程和主线程会合,这时主线程继续执行等待操作之后的下一步操作。主线程必须会合可会合的子线程。在主线程的线程函数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否则,系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。
相分离(detached):表示子线程无需和主线程会合,也就是相分离的,这种情况下,子线程一旦进入终止状态,这种方式常用在线程数较多的情况下,有时让主线程逐个等待子线程结束,或者让主线程安排每个子线程结束的等待顺序,是很困难或不可能的,所以在并发子线程较多的情况下,这种方式也会经常使用。

举一个例子来简单来介绍线程

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <errno.h>
  4 #include <stdlib.h>
  5 #include <unistd.h>
  6 #include <pthread.h>
  7 
  8 void *thread_worker1(void *args);
  9 void *thread_worker2(void *args);
 10 
 11 int main(int argc, char **argv)
 12 {
 13         int                      share_var = 1000;
 14         pthread_t                tid;
 15         pthread_attr_t           thread_attr;
 16 
 17 
 18         if( pthread_attr_init(&thread_attr) )
 19         {
 20                 printf("pthread_attr_init() failure: %s\n", strerror(errno));
 21                 return -1;
 22         }
 23 
 24         if( pthread_attr_setstacksize(&thread_attr, 120*1024) )
 25         {
 26                 printf("pthread_attr_setstacksize() failure: %s\n", strerror(errno));
 27                 return -1;
 28         }
 29 
 30         if( pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED) )
 31         {
 32                 printf("pthread_attr_setdetachstate() failure: %s\n", strerror(errno));
 33                 return -1;
 34         }
 35 
 36         pthread_create(&tid, &thread_attr, thread_worker1, &share_var);
 37         printf("Pthread worker1 tid[%ld] create ok\n", tid);
 38 
 39         pthread_create(&tid, NULL, thread_worker2, &share_var);
 40         printf("Pthread worker2 tid[%ld] create ok\n", tid);
 41 
 42         pthread_attr_destroy(&thread_attr);
 43 
 44         pthread_join(tid, NULL);
 45 
 46 
 47         while(1)
 48         {
 49                 printf("Mian/Control thread share_var: %d\n", share_var);
 50                 sleep(10);
 51         }
 52 }
 53 
 54 void *thread_worker1(void *args)
 55 {
 56         int             *ptr = (int *)args;
 57 
 58         if( !args )
 59         {
 60                 printf(" %s() get invalid arguments\n", __FUNCTION__);
 61                 pthread_exit(NULL);
 62         }
 63 
 64         printf("Thread worker 1 [%ld] start running...\n", pthread_self());
 65 
 66         while(1)
 67         {
 68                 printf("+++: %s before share_var++: %d\n", __FUNCTION__, *ptr);
 69                 *ptr +=1;
 70                 sleep(2);
 71                 printf("+++: %s after sleep shared_var: %d\n", __FUNCTION__, *ptr);
 72         }
 73 
 74         printf("Thread worker1 exit...\n");
 75 
 76         return NULL;
 77 }
 78 
 79 void *thread_worker2(void *args)
 80 {
 81         int               *ptr = (int *)args;
 82 
 83         if( !args )
 84         {
 85                 printf("%s() get invalid arguments\n", __FUNCTION__);
 86                 pthread_exit(NULL);
 87         }
 88 
 89         printf("Thread worker 2 [%ld] start running...\n", pthread_self());
 90 
 91 
 92         while(1)
 93         {
 94                 printf("---: %s before shared_var++: %d\n", __FUNCTION__, *ptr);
 95                 *ptr += 1;
 96                 sleep(2);
 97                 printf("---: %s after sleep shared_var: %d\n", __FUNCTION__, *ptr);
 98         }
 99 
100         printf("Thread worker2 exit...\n");
101 
102         return NULL;
103 }

在这里插入图片描述

三、锁

阻塞锁

多个线程同时调用同一个方法的时候,所有线程都被排队处理了。让线程进入阻塞状态进行等待,当获得相应的信号(唤醒,时间) 时,才可以进入线程的准备就绪状态,准备就绪状态的所有线程,通过竞争,进入运行状态。

非阻塞锁

多个线程同时调用一个方法的时候,当某一个线程最先获取到锁,这时其他线程判断没拿到锁,这时就直接返回,只有当最先获取到锁的线程释放,其他线程才能进来,在它释放之前其它线程都会获取失败。

死锁

如果多个线程要调用多个对象,则在上锁的时候可能会出现“死锁”。举个例子: A、B两个线程会同时使用到两个共享变量m和n,同时每个变量都有自己相应的锁M和N。 这时A线程首先拿到M锁访问m,接下来他需要拿N锁来访问变量n; 而如果此时B线程拿着N锁等待着M锁的话,就造成了线程“死锁”。
在这里插入图片描述
死锁产生的4个必要条件
1、互斥:某种资源一次只允许一个进程访问,即该资源一旦分配给某个进程,其他进程就不能再访问,直到该进程访问结束。
2、占有且等待:一个进程本身占有资源(一种或多种),同时还有资源未得到满足,正在等待其他进程释放该资源。
3、不可抢占:别人已经占有了某项资源,你不能因为自己也需要该资源,就去把别人的资源抢过来。
4、循环等待:存在一个进程链,使得每个进程都占有下一个进程所需的至少一种资源。
当以上四个条件均满足,必然会造成死锁,发生死锁的进程无法进行下去,它们所持有的资源也无法释放。这样会导致CPU的吞吐量下降。所以死锁情况是会浪费系统资源和影响计算机的使用性能的。那么,解决死锁问题就是相当有必要的了。
产生死锁需要四个条件,那么,只要这四个条件中至少有一个条件得不到满足,就不可能发生死锁了。由于互斥条件是非共享标题资源所必须的,不仅不能改变,还应加以保证,所以,主要是破坏产生死锁的其他三个条件。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值