精华内容
参与话题
问答
  • Linux线程

    千次阅读 2019-04-23 15:43:32
    Linux线程_01线程介绍和常用系统调用1.Linux线程1.1进程回顾1.2程序并发的时空开销1.3线程的的特点2.POSIX线程常用系统调用2.1线程创建与回收2.2线程取消2.3线程函数退出相关2.4获取线程id 1.Linux线程 如果说在操作...

    1.Linux线程

    如果说在操作系统中引入进程是为了使多个程序可以并发执行,以提高资源利用率和系统吞吐量,那么在操作系统中引入线程则是为了减少程序哎并发执行时所付出的时空开销,使OS具有更好的并发性。

    1.1进程回顾

    • 进程是一个可以拥有资源的独立单位,它所拥有的资源包括用于存放程序的磁盘和内存地址空间以及它在运行时所需要的IO设备、已经打开的文件、信号量等。
    • 传统OS中,进程也是一个可独立调度和分派的基本单位,每个进程在系统中有唯一的PCB,系统可以根据PCB感知进程的存在,咋利用PCB中的信息来恢复进程运行的现场。

    以上两个进程的基本属性使进程成为一个能独立运行的基本单位,也就构成了程序并发执行的基础。

    1.2程序并发的时空开销

    • 1.创建进程:系统创建进程时为其分配其需要的资源。
    • 2.撤销进程:系统撤销进程时回收其所占资源和PCB。
    • 3.进程切换:切换进程时系统会保留当前进程CPU环境,为新进程设置CPU环境。

    进程技术的劣势就是进程间切换开销大,进程间通信麻烦而且效率低

    1.3线程的的特点

    与进程(process)类似,线程(thread)是允许程序并发执行多个任务的一种机制。一个进程可以包含多个线程,统一程序中的每个线程都可以独立的执行相同程序。(传统意义上的UNIX进程只是多线程程序的特例,该进程只包含一个线程)在引入线程的OS中,线程作为调度和分派的基本单位,可以有效地改善多处理机系统性能。线程具有像进程一样可被OS调度同一进程的多个线程之间很容易高效率通信在多核心CPU(对称多处理器架构SMP)架构下效率最大化等优势。

    • 同一进程中的线程可以并发执行。
    • 线程本身并不拥有资源,而是仅有一点必不可少的、能保证其独立运行的资源,如用于控制线程运行的线程控制块TCB等。
    • 同一进程中的线程共享进程的内存地址空间和资源。
    • 线程也有三种状态:执行状态、就绪状态、阻塞状态。

    2.POSIX线程常用系统调用

    启动程序时,产生的进程只有单条线程,称为初始线程或主线程

    2.1线程创建与回收

    • pthread_create() 创造子线程
    #include <pthread.h>
    int pthread_create(pthread_t *thread, 
    				   const pthread_attr_t *attr,
                       void *(*start_routine) (void *), 
                       void *arg);
    

    其中thread为指向线程标识符的指针,attr用来设置线程属性,剩下的两个参数分别是该线程执行的函数和函数的参数。

    • pthread_join() 等待(阻塞)回收子线程
    #include <pthread.h>
    int pthread_join(pthread_t thread, void **retval);
    

    thread为被等待的线程标识符,retval为一个用户定义的指针,它可以用来存储被等待线程的返回值。

    • pthread_detach() 分离子线程,分离后主线程不必再去回收子线程
    #include <pthread.h>
    int pthread_detach(pthread_t thread);
    

    thread为被分离的子线程的标识符。

    pthread_join()和pthread_detach()不能同时都选,也不能同时都不选。

    2.2线程取消

    取消就是强制终止线程。

    • pthread_cancel() 取消子线程
    #include <pthread.h>
    int pthread_cancel(pthread_t thread);
    

    thread为被取消的子线程的标识符。

    • pthread_setcancelstate()和 pthread_setcanceltype() 子线程设置自己是否允许被取消
    #include <pthread.h>
    int pthread_setcancelstate(int state, int *oldstate);
    int pthread_setcanceltype(int type, int *oldtype);
    

    state参数有两个选项PTHREAD_CANCEL_ENABLE、PTHREAD_CANCEL_DISABLE分别是可以取消和不可取消。pthread_setcanceltype()在state是enable的情况下才有意义,type对于两个参数PTHREAD_CANCEL_DEFERRED子线程不能立即终止和PTHREAD_CANCEL_ASYNCHRONOUS子线程任何时候都立即终止。

    2.3线程函数退出相关

    • pthread_exit()或函数里return返回指定值 线程正常退出
    #include <pthread.h>
    void pthread_exit(void *retval);
    
    • pthread_cleanup_push()
    #include <pthread.h>
    void pthread_cleanup_push(void (*routine)(void *),void *arg);
    
    • pthread_cleanup_pop()
    #include <pthread.h>
    void pthread_cleanup_pop(int execute);
    

    2.4获取线程id

    • pthread_self()
    #include <pthread.h>
    pthread_t pthread_self(void);
    
    展开全文
  • Linux 线程

    2016-08-24 17:43:29
    Linux线程是针对POSIX线程,也就是pthread,Linux对它的支持最好。  线程是一个更接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。在串行程序的基础上引入...
       线程(thread)是在 共享内存空间 中并发的多道执行路径,它们共享一个进程资源,如文件描述符和信号处理。Linux的线程是针对POSIX线程,也就是pthread,Linux对它的支持最好。
    
       线程是一个更接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。在串行程序的基础上引入线程和进程是为了提高程序的并发度,从而提高程序的运行效率和响应时间。
       也可以将线程和轻量级进程(LWP)视为同等,但LWP更恰当的解释为一个虚拟CPU或内核的线程。它可以帮助用户态线程实现一些特殊的功能。
       Pthread是一种标准化模型,他把一个程序分成一组能够执行的任务。
    一般,下列三种场合会使用Pthread线程:
    • 在返回前 阻塞的I/O任务 能够使用一个线程处理I/O, 同时处理其他任务。
    • 在有一个或多个任务受不确定性事件影响,比如网络通信的可获得性影响的场合,能够使用线程处理这些异步事件,同时执行正常的处理。
    • 如果程序的某些功能比其他功能更重要,可以使用线程以保证所有功能都出现。但那些时间密集型的功能具有更高的优先级。 
      总结: 在检查程序中潜在的并行性时,也就是说在要找出能够同时执行任务时使用Pthread
     
      Linux进程模型提供了执行多个进程的能力,已经可以进行并行或者并发编程。可是,线程能够让你对多个任务的控制度更好、使用资源更少,因为一个单一的资源,如全局变量,可以由多个线程共享。而且,在拥有多个处理器的系统上。多线程应该比用多个进程实现的应用的执行速度更快!
        线程和进程在使用上各有优缺点:
    • 线程执行开销小,但不利于资源的管理和保护;而进程正相反。
    • 线程适合在对称多处理器的计算机上运行,而进程则可以跨机器迁移;
    • 进程可以拥有资源,而线程共享进程拥有的资源;
    • 进程的切换必须保存在进程控制块中(PCB,Process Control Block),同个进程的多个线程间的切换不用那么麻烦。
            
    总结:
         进程单独占有系统资源,进程的切换必须保存在进程控制块(PCB)中;线程共享进程的资源,(同个进程的)线程之间的切换不那么麻烦。线程是一个更接近于执行体的概念,它可以同进程中的其他线程共享数据,但拥有自己的栈空间拥有独立的执行序列



    展开全文
  • 【Linux系统编程】Linux线程浅析

    千次阅读 2019-11-08 18:09:04
    进程和线程区别02. LinuxThreads03. NPTL04. NGPT05. 附录 01. 进程和线程区别 在许多经典的操作系统教科书中,总是把进程定义为程序的执行实例,它并不执行什么, 只是维护应用程序所需的各种资源,而线程则是真正...

    00. 目录

    01. 进程和线程区别

    在许多经典的操作系统教科书中,总是把进程定义为程序的执行实例,它并不执行什么, 只是维护应用程序所需的各种资源,而线程则是真正的执行实体。

    为了让进程完成一定的工作,进程必须至少包含一个线程。

    进程,直观点说,保存在硬盘上的程序运行以后,会在内存空间里形成一个独立的内存体,这个内存体有自己的地址空间,有自己的堆,上级挂靠单位是操作系统。操作系统会以进程为单位,分配系统资源,所以我们也说,进程是资源分配的最小单位。

    线程存在与进程当中,是操作系统调度执行的最小单位。说通俗点,线程就是干活的。

    如果说进程是一个资源管家,负责从主人那里要资源的话,那么线程就是干活的苦力。一个管家必须完成一项工作,就需要最少一个苦力,也就是说,一个进程最少包含一个线程,也可以包含多个线程。苦力要干活,就需要依托于管家,所以说一个线程,必须属于某一个进程。进程有自己的地址空间,线程使用进程的地址空间,也就是说,进程里的资源,线程都是有权访问的,比如说堆啊,栈啊,静态存储区什么的。

    线程就是个无产阶级,但无产阶级干活,总得有自己的劳动工具吧,这个劳动工具就是栈,线程有自己的栈,这个栈仍然是使用进程的地址空间,只是这块空间被线程标记为了栈。每个线程都会有自己私有的栈,这个栈是不可以被其他线程所访问的。

    进程所维护的是程序所包含的资源(静态资源), 如:地址空间,打开的文件句柄集,文件系统状态,信号处理handler,等;

    线程所维护的运行相关的资源(动态资源),如:运行栈,调度相关的控制信息,待处理的信号集,等;

    然而,一直以来,linux 内核并没有线程的概念。每一个执行实体都是一个 task_struct 结构,通常称之为进程。

    进程是一个执行单元,维护着执行相关的动态资源。同时,它又引用着程序所需的静态资源。通过系统调用 clone 创建子进程时,可以有选择性地让子进程共享父进程所引用的资源,这样的子进程通常称为轻量级进程。

    linux 上的线程就是基于轻量级进程,由用户态的 pthread 库实现的。使用 pthread 以后,在用户看来,每一个 task_struct 就对应一个线程,而一组线程以及它们所共同引用的一组资源就是一个进程。

    但是,一组线程并不仅仅是引用同一组资源就够了,它们还必须被视为一个整体。

    对此,POSIX 标准提出了如下要求:

    1)查看进程列表的时候,相关的一组 task_struct 应当被展现为列表中的一个节点;

    2)发送给这个“进程”的信号(对应 kill 系统调用), 将被对应的这一组 task_struct 所共享,并且被其中的任意一个“线程”处理;

    3)发送给某个“线程”的信号(对应 pthread_kill), 将只被对应的一个task_struct接收,并且由它自己来处理;

    4)当“进程”被停止或继续时(对应 SIGSTOP/SIGCONT 信号), 对应的这一组 task_struct 状态将改变;

    5)当“进程”收到一个致命信号(比如由于段错误收到 SIGSEGV 信号), 对应的这一组 task_struct 将全部退出;

    6)等等(以上可能不够全)

    02. LinuxThreads

    在 linux 2.6 以前,pthread 线程库对应的实现是一个名叫 LinuxThreads 的 lib。

    LinuxThreads 利用前面提到的轻量级进程来实现线程,但是对于 POSIX 提出的那些要求,LinuxThreads 除了第 5 点以外(当“进程”收到一个致命信号(比如由于段错误收到 SIGSEGV 信号), 对应的这一组 task_struct 将全部退出),都没有实现(实际上是无能为力):

    1)如果运行了 A 程序,A 程序创建了 10 个线程,那么在 shell 下执行 ps 命令时将看到 11 个 A 进程,而不是 1 个(注意, 也不是10个,下面会解释);

    2)不管是 kill 还是 pthread_kill,信号只能被一个对应的线程所接收;

    3)SIGSTOP/SIGCONT 信号只对一个线程起作用;

    还好 LinuxThreads 实现了第 5 点,我认为这一点是最重要的。如果某个线程“挂”了,整个进程还在若无其事地运行着,可能会出现很多的不一致状态。进程将不是一个整体,而线程也不能称为线程。

    或许这也是为什么 LinuxThreads 虽然与 POSIX 的要求差距甚远,却能够存在,并且还被使用了好几年的原因吧~

    但是,LinuxThreads 为了实现这个“第 5 点”, 还是付出了很多代价,并且创造了 LinuxThreads 本身的一大性能瓶颈。

    接下来要说说,为什么 A 程序创建了 10 个线程,但是 ps 时却会出现 11 个 A 进程了。 因为 LinuxThreads 自动创建了一个管理线程。上面提到的“第5点”就是靠管理线程来实现的。

    当程序开始运行时, 并没有管理线程存在(因为尽管程序已经链接了 pthread 库, 但是未必会使用多线程)。 程序第一次调用 pthread_create 时,LinuxThreads 发现管理线程不存在,于是创建这个管理线程。这个管理线程是进程中的第一个线程(主线程)的儿子。然后在 pthread_create 中,会通过 pipe 向管理线程发送一个命令,告诉它创建线程。即是说,除主线程外,所有的线程都是由管理线程来创建的,管理线程是它们的父亲。

    于是,当任何一个子线程退出时,管理线程将收到 SIGUSER1 信号(这是在通过 clone 创建子线程时指定的)。管理线程在对应的 sig_handler 中会判断子线程是否正常退出,如果不是,则杀死所有线程,然后自杀。

    那么,主线程怎么办呢? 主线程是管理线程的父亲,其退出时并不会给管理线程发信号。 于是,在管理线程的主循环中通过 getppid 检查父进程的 ID 号,如果 ID 号是 1,说明父亲已经退出,并把自己托管给了 init 进程(1 号进程)。这时候,管理线程也会杀掉所有子线程,然后自杀。

    那么,如果主线程是调用 pthread_exit 主动退出的呢? 按照 posix 的标准,这种情况下其他子线程是应该继续运行的。于是,在 LinuxThreads 中,主线程调用 pthread_exit 以后并不会真正退出,而是会在 pthread_exit 函数中阻塞等待所有子线程都退出了, pthread_exit 才会让主线程退出。(在这个等等过程中,主线程一直处于睡眠状态。)

    可见,线程的创建与销毁都是通过管理线程来完成的,于是管理线程就成了 LinuxThreads 的一个性能瓶颈。线程的创建与销毁需要一次进程间通信,一次上下文切换之后才能被管理线程执行,并且多个请求会被管理线程串行地执行。

    03. NPTL

    到了 linux 2.6,glibc 中有了一种新的 pthread 线程库 —— NPTL(Native POSIX Threading Library)。

    NPTL 实现了前面提到的 POSIX 的全部5点要求:

    1)查看进程列表的时候,相关的一组 task_struct 应当被展现为列表中的一个节点;

    2)发送给这个“进程”的信号(对应 kill 系统调用), 将被对应的这一组 task_struct 所共享,并且被其中的任意一个“线程”处理;

    3)发送给某个“线程”的信号(对应 pthread_kill), 将只被对应的一个task_struct接收,并且由它自己来处理;

    4)当“进程”被停止或继续时(对应 SIGSTOP/SIGCONT 信号), 对应的这一组 task_struct 状态将改变;

    5)当“进程”收到一个致命信号(比如由于段错误收到 SIGSEGV 信号), 对应的这一组 task_struct 将全部退出;

    但是,实际上,与其说是 NPTL 实现了,不如说是linux内核实现了。

    在 linux 2.6 中,内核有了线程组的概念,task_struct 结构中增加了一个 tgid(thread group id)字段。 如果这个 task 是一个“主线程”, 则它的 tgid 等于 pid,否则 tgid 等于进程的 pid(即主线程的 pid)。

    在 clone 系统调用中,传递 CLONE_THREAD 参数就可以把新进程的 tgid 设置为父进程的 tgid(否则新进程的 tgid 会设为其自身的 pid)。

    类似的 XXid 在 task_struct 中还有两个:task->signal->pgid 保存进程组的打头进程的 pid、task->signal->session 保存会话打头进程的 pid 。通过这两个 id 来关联进程组和会话。

    有了 tgid,内核或相关的 shell 程序就知道某个 tast_struct 是代表一个进程还是代表一个线程,也就知道在什么时候该展现它们,什么时候不该展现(比如在 ps 的时候,线程就不要展现了)。而 getpid(获取进程 ID)系统调用返回的也是 tast_struct 中的 tgid,而 tast_struct 中的 pid 则由 gettid 系统调用来返回。

    在执行 ps 命令的时候不展现子线程,也是有一些问题的。比如程序 a.out 运行时,创建了一个线程。假设主线程的 pid 是 10001、子线程是 10002(它们的 tgid 都是10001)。这时如果你 kill 10002,是可以把 10001 和 10002 这两个线程一起杀死的,尽管执行 ps 命令的时候根本看不到 10002 这个进程。如果你不知道 linux 线程背后的故事,肯定会觉得遇到灵异事件了。

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <pthread.h>
     
    void *fun(void *arg)
    {
    	printf("thread is created!\n");
    	pause(); //挂起线程
    }
     
    int main(void)
    {
    	pthread_t tid;
    	pthread_create(&tid, NULL, fun, NULL);
    	pause();//主线程挂起(否则主线程终止,子线程也就挂了)
    	
    	return 0;
    }
    

    这个程序创建一个线程后挂起,子线程在输出 “thread is created!” 也挂起。运行结果如下

    deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c -pthread 
    deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  
    thread is created!
    

    我们打开另外一个终端,查看后台运行的进程,发现 a.out的进程号(pid)是 4183,我们使用 kill 终止 pid 为 4184的进程(注意 ps 中并没有这个进程),如下

    deng@itcast:~$ ps aux | grep a.out
    deng       4183  0.0  0.0  80424   876 pts/3    Sl+  18:02   0:00 ./a.out
    deng       4186 12.0  0.0  21536  1056 pts/4    S+   18:02   0:00 grep --color=auto a.out
    deng@itcast:~$ kill 4184
    deng@itcast:~$ 
    

    结果发现,demo 进程也终止了,如下图。其原因就是 2362 就是所创建线程的线程号,线程异常终止了,其对应的进程也就终止了。

    deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c -pthread 
    deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  
    thread is created!
    已终止
    deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 
    

    为了应付“发送给进程的信号”和“发送给线程的信号”, task_struct 里面维护了两套 signal_pending,一套是线程组共享的,一套是线程独有的。

    通过 kill 发送的信号被放在线程组共享的 signal_pending 中,可以由任意一个线程来处理;通过 pthread_kill 发送的信号(pthread_kill 是 pthread 库的接口,对应的系统调用中 tkill)被放在线程独有的 signal_pending 中, 只能由本线程来处理。

    当线程停止/继续,或者是收到一个致命信号时,内核会将处理动作施加到整个线程组中。

    04. NGPT

    说到这里,也顺便提一下 NGPT(Next Generation POSIX Threads)。

    上面提到的两种线程库使用的都是内核级线程(每个线程都对应内核中的一个调度实体),这种模型称为 1:1 模型(1 个线程对应 1 个内核级线程);

    而 NGPT 则打算实现 M:N 模型(M 个线程对应 N 个内核级线程),也就是说若干个线程可能是在同一个执行实体上实现的。 线程库需要在一个内核提供的执行实体上抽象出若干个执行实体,并实现它们之间的调度。这样被抽象出来的执行实体称为用户级线程。

    大体上,这可以通过为每个用户级线程分配一个栈,然后通过 longjmp 的方式进行上下文切换。(百度一下"setjmp,longjmp", 你就知道。)

    但是实际上要处理的细节问题非常之多,目前的 NGPT 好像并没有实现所有预期的功能,并且暂时也不准备去实现。

    用户级线程的切换显然要比内核级线程的切换快一些,前者可能只是一个简单的长跳转,而后者则需要保存/装载寄存器,进入然后退出内核态。(进程切换则还需要切换地址空间等)

    而用户级线程则不能享受多处理器,因为多个用户级线程对应到一个内核级线程上,一个内核级线程在同一时刻只能运行在一个处理器上。

    不过,M:N 的线程模型毕竟提供了这样一种手段,可以让不需要并行执行的线程运行在一个内核级线程对应的若干个用户级线程上,可以节省它们的切换开销。

    据说一些类 UNIX 系统(如 Solaris)已经实现了比较成熟的 M:N 线程模型,其性能比起 linux 的线程还是有着一定的优势。

    05. 附录

    5.1 参考:linux线程浅析

    展开全文
  • Linux线程同步

    千次阅读 2020-03-06 09:20:48
    文章目录一、线程同步的概念二、互斥锁1、初始化锁2、阻塞加锁3、非阻塞加锁4、解锁5、销毁锁(此时锁必需unlock状态,否则返回EBUSY)三、示例程序四、版权声明 一、线程同步的概念 线程同步?怎么同步?一起运行?...

    一、线程同步的概念

    线程同步?怎么同步?一起运行?一起停止?我当年听说线程同步这个词的时候,也是一头雾水。

    在人们的日常生活中的锁大概有两种:一种是不允许访问;另一种是资源忙,同一时间只允许一个使用者占用,其它使用者必须要等待。

    1)不允许访问的锁容易理解,就像每家每户的门锁,不允许外人进入。

    2)第二种锁,例如火车上的厕所,它是公共的,空闲的时候任何人可以进入,人进去以后就会把它锁起来,其它的人如果要上厕所,必须等待解锁,即里面的人出来。还有红绿灯,红灯是加锁,绿灯是解锁。

    对多线程来说,资源是共享的,基本上不存在不允许访问的情况,但是,共享的资源在某一时间点只能有一个线程占用,所以需要给资源加锁。

    不知道是什么人采用了线程同步这个词,如果让我的命名,我会定义为线程锁,锁线程吗?不是,是锁共享资源,线程给共享资源加的锁。

    线程的锁的种类有互斥锁、读写锁、条件变量、自旋锁、信号灯。

    在本章节中,只介绍互斥锁,其它的锁应用场景复杂,开发难度很大,不合适初学者。

    二、互斥锁

    互斥锁机制是同一时刻只允许一个线程占有共享的资源。

    1、初始化锁

    int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutex_attr_t *mutexattr);
    

    其中参数 mutexattr 用于指定锁的属性(见下),如果为NULL则使用缺省属性。

    互斥锁的属性在创建锁的时候指定,当资源被某线程锁住的时候,其它的线程在试图加锁时表现将不同。当前有四个值可供选择:

    1)PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。

    2)PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。

    3)PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。

    4)PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,等待解锁后重新竞争。

    2、阻塞加锁

    int pthread_mutex_lock(pthread_mutex *mutex);
    

    如果是锁是空闲状态,本线程将获得这个锁;如果锁已经被占据,本线程将排队等待,直到成功的获取锁。

    3、非阻塞加锁

    int pthread_mutex_trylock( pthread_mutex_t *mutex);
    

    该函数语义与 pthread_mutex_lock() 类似,不同的是在锁已经被占据时立即返回
    EBUSY,不是挂起等待。

    4、解锁

    int pthread_mutex_unlock(pthread_mutex *mutex);
    

    线程把自己持有的锁释放。

    5、销毁锁(此时锁必需unlock状态,否则返回EBUSY)

    int pthread_mutex_destroy(pthread_mutex *mutex);
    

    销毁锁之前,锁必需是空闲状态(unlock)。

    三、示例程序

    多线程可以共享资源(变量和对象),对编程带来了方便,但是某些对象虽然可以共享,但在同一个时间只能由一个线程使用,多个线程同时使用会产生冲突,例如socket连接,数据库连接池。

    我们把前几章节的socket客户端程序book247.cpp修改为多线程。

    示例(book263.cpp)

    /*
     * 程序名:book263.cpp,此程序用于演示多线程的互斥锁
     * 作者:C语言技术网(www.freecplus.net) 日期:20190525
    */
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <pthread.h>
    
    //xx pthread_mutex_t mutex; // 申明一个互斥锁
    
    // 与客户端通信线程的主函数
    void *pth_main(void *arg)
    {
      int pno=(long)arg;   // 线程编号
    
      pthread_detach(pthread_self());
    
      char strbuffer[1024];
      
      for (int ii=0;ii<3;ii++)    // 与服务端进行3次交互。
      {
        //xx pthread_mutex_lock(&mutex);  // 加锁
        memset(strbuffer,0,sizeof(strbuffer));
        sprintf(strbuffer,"线程%d:这是第%d个超级女生,编号%03d。",pno,ii+1,ii+1);
        if (TcpClient.Send(strbuffer,strlen(strbuffer))<=0) break;
        printf("发送:%s\n",strbuffer);
    
        memset(strbuffer,0,sizeof(strbuffer));
        if (TcpClient.Recv(strbuffer,sizeof(strbuffer))<=0) break;
        printf("线程%d接收:%s\n",pno,strbuffer);
        //xx pthread_mutex_unlock(&mutex);  // 释放锁
        // usleep(100);   // usleep(100),否则其它的线程无法获得锁。
      }
    
      pthread_exit(0);
    }
    
    int main()
    {
      // 向服务器发起连接请求
      if (TcpClient.ConnectToServer("172.16.0.15",5051)==false)
      { printf("TcpClient.ConnectToServer(\"172.16.0.15\",5051) failed,exit...\n"); return -1; }
    
      //xx pthread_mutex_init(&mutex,0); // 创建锁
    
      pthread_t pthid1,pthid2;
      pthread_create(&pthid1,NULL,pth_main,(void*)1);   // 创建第一个线程
      pthread_create(&pthid2,NULL,pth_main,(void*)2);   // 创建第二个线程
    
      pthread_join(pthid1,NULL);    // 等待线程1退出。
      pthread_join(pthid2,NULL);    // 等待线程2退出。
    
      //xx pthread_mutex_lock(&mutex);   // 销毁锁
    }
    

    在book263.cpp程序中,客户端成功连上服务器后,创建两个线程,同时与服务端进行通信,发送3个请求报文并接收服务端的回应。

    book263.cpp暂时不启用锁,先试试效果。

    启动服务端程序book261,然后再启动book263。

    运行效果

    在这里插入图片描述

    大家仔细研究一下book263运行的结果,可以发现客户端的两个线程的报文收发出现了混乱。

    把book263.cpp的线程锁代码启用,编译运行。

    运行效果

    在这里插入图片描述
    非常棒,这正在我们想要的结果。

    四、版权声明

    C语言技术网原创文章,转载请说明文章的来源、作者和原文的链接。
    来源:C语言技术网(www.freecplus.net)
    作者:码农有道

    如果这篇文章对您有帮助,请点赞支持,或在您的博客中转发我的文章,谢谢!!!
    如果文章有错别字,或者内容有错误,或其他的建议和意见,请您留言指正,非常感谢!!!

    展开全文
  • linux线程互斥量pthread_mutex_t使用简介

    万次阅读 多人点赞 2018-06-03 20:30:42
    在多线程应用程序中,当多个线程共享相同的内存时,如同时访问一个变量时,需要确保每个线程看到一致的数据视图,即保证所有线程对数据的修改是一致的。 如下两种情况不存在不一致的问题: 每个线程使用的变量都...
  • 【Linux】Linux线程私有数据

    千次阅读 2018-09-10 16:52:08
    线程私有数据 在单线程程序中,函数经常使用全局变量或静态变量,这是不会影响程序的正确性的,但如果线程调用的函数使用全局变量或静态变量,则很可能引起错误。因为这些函数使用的全局变量和静态变量无法为不同的...
  • Linux线程详解

    千次阅读 多人点赞 2019-06-03 12:06:33
    并行和并发的区别 1. 并发(concurrency):在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行。其中两种并发关系分别是同步和互斥。...
  • Linux线程模型

    千次阅读 2019-02-28 22:53:19
    一直以来, linux内核并没有线程的概念.每一个执行实体都是一个task_struct结构,通常称之为进程.Linux内核在2.0.x版本就已经实 现了轻量进程,应用程序可以通过一个统一的clone()系统调用接口,用不同的参数指定创建...
  • Linux线程--设置线程名字

    千次阅读 2019-08-17 22:13:45
    #include <stdio.h> #include <unistd.h> #include <pthread.h> #include <sys/prctl.h> static void *test_thread(void *arg) { int val = *((int *)arg);... struct sched_param...
  • Linux线程(七)

    千次阅读 2020-01-17 12:52:59
    Linux线程(七) 一、单例模式 1.什么是单例模式? 单例模式是一种经典的“设计模式”,保证类在内存中只能有一个对象 2.什么是设计模式? 设计模式是一套被**反复使用的、多数人知晓的、经过分类编目的、代码设计...
  • Linux线程(三)

    千次阅读 2020-01-11 21:54:42
    Linux线程(三) 一、互斥量 根据前面的分析,得到的结果不是我们想要的原因是–ticket操作不是原子操作,这个共享资源可能并发的切换大其他线程,导致有多个线程同时影响到这个共享资源,所以导致得到的结果不对...
  • linux 线程切换

    2018-10-11 22:35:21
    1.什么引起线程切换 时间片轮转 线程阻塞 线程主动放弃时间片
  • Linux线程(二)

    千次阅读 2020-01-11 20:04:50
    linux线程(二) 一、线程终止: 1.pthread_exit函数: 只需终止某个线程而不需要终止整个进程的三个方法: 从线程函数return,这种方法对主线程不适合,从main函数的return,相当于调用exit函数 线程可以调用...
  • Linux线程之线程 线程组 进程的关系

    千次阅读 2018-09-19 17:44:02
    一直对于线程 线程组 进程 理解不是很深刻,今天看了这篇文章,感觉讲解很透彻。 转载出处:http://www.it165.net/os/html/201305/5123.html#about 然后看到例子中如果对于gettid()和 pthread_self() 函数不熟悉的...
  • Linux线程(一)

    千次阅读 2020-01-11 16:57:20
    Linux线程(一) 一、线程相关概念? 1.什么是线程: 线程是一个程序内的一个执行路线。更准确的说是线程是“一个进程内的控制序列“ 任意的进程至少有一个执行线程 线程在进程内部执行,本质是在进程地址空间内...
  • Linux 线程线程自杀

    2018-03-11 11:33:41
    因为项目需要,在处理线程获取资源以及关闭资源的获取时,使用到pthread_cancel函数。至于工作的具体代码,属于保密,自己写了一个小的测试代码。代码如下:#include&lt;stdio.h&gt;#include&lt;stdlib....
  • linux线程随笔-pthread_create函数

    万次阅读 2018-08-18 20:15:16
     pthread_create是UNIX环境创建线程函数 头文件  #include&lt;pthread.h&gt; 函数声明  int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict_attr, void*(*start_rtn...
  • Linux 线程ID

    千次阅读 2018-04-03 23:18:45
    但现在我们引入了线程的概念后,一个用户进程可以包含多个用户态线程,每个线程作为一个独立的调度实体在内核态都有自己的进程描述符PCB,因此Linux内核为了处理以上关系,引入线程组的概念。 我们之前在学习进程的...
  • Linux 线程锁详解

    千次阅读 2018-11-08 00:50:54
    Linux 线程锁详解
  • linux线程内存开销

    千次阅读 2018-12-10 17:21:07
    1.首先是线程自己栈,程序没设置过,就是默认的,ulimit -s 中的值,现在一般都是10240(单位KB)。 2.跟版本有关,是否有 glibc 的 malloc per thread arenas 特性,有了这个特性,设置不好,一个新线程要增加至少64...
  • Linux线程与信号

    2018-12-01 15:17:30
    1信号与线程的关系 POSIX标准对多线程情况下的信号机制提出了一些要求: 信号处理函数必须在多线程进程的所有线程之间共享, 但是每个线程要有自己的挂起信号集合和阻塞信号掩码。 POSIX函数kill/sigqueue必须...

空空如也

1 2 3 4 5 ... 20
收藏数 43,526
精华内容 17,410
关键字:

linux 线程

linux 订阅