Linux c++多线程问题

因项目问题,最近涉及到一些跟线程相关的知识,总结一下 大家互相进步。

在传统的Unix模型中,当一个进程需要由另一个实体执行某件事时,该进程派生(fork)一个子进程,让子进程去进行处理。Unix下的大多数网络服务器程序都是这么编写的,即父进程接受连接,派生子进程,子进程处理与客户的交互。

虽然这种模型很多年来使用得很好,但是fork时有一些问题:

  • fork是昂贵的。内存映像要从父进程拷贝到子进程,所有描述字要在子进程中复制等等。目前有的Unix实现使用一种叫做写时拷贝(copy-on-write)的技术,可避免父进程数据空间向子进程的拷贝。尽管有这种优化技术,fork仍然是昂贵的。
  • 2. fork子进程后,需要用进程间通信(IPC)在父子进程之间传递信息。Fork之前的信息容易传递,因为子进程从一开始就有父进程数据空间及所有描述字的拷贝。但是从子进程返回信息给父进程需要做更多的工作。

线程有助于解决这两个问题。线程有时被称为轻权进程(lightweight process),因为线程比进程“轻权”,一般来说,创建一个线程要比创建一个进程快10~100倍。

    一个进程中的所有线程共享相同的全局内存,这使得线程很容易共享信息,但是这种简易性也带来了同步问题。

一个进程中的所有线程不仅共享全局变量,而且共享:进程指令、大多数数据、打开的文件(如描述字)、信号处理程序和信号处置、当前工作目录、用户ID和组ID。但是每个线程有自己的线程ID、寄存器集合(包括程序计数器和栈指针)、栈(用于存放局部变量和返回地址)、error、信号掩码、优先级。在Linux中线程编程符合Posix.1标准,称为Pthreads。所有的pthread函数都以pthread_开头。在调用它们前均要包括pthread.h头文件,一个函数库libpthread.a来实现。

线程的属性


  • 结构体以及头文件

线程属性结构为pthread_attr_t,在头文件/usr/include/pthread.h中定义。初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。

  • 线程绑定

设置线程绑定状态的函数为pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值:PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。下面的代码即创建了一个绑定的线程。  

    轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。。     

  • 线程分离

线程的分离状态决定一个线程以什么样的方式来终止自己。原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程

  • 线程优先级

优先级的设定一般为先获取在修改,修改线程优先级需要注意到创建进程的类型。

SCHED_OTHER 表示 创建的普通进程。
SCHED_FIFO&&SCHED_RR表示创建的可修改优先级的线程,高优先级会抢占低优先级。 
前者,高优先级运行期间,低优先级没法抢占,只能等到高优先级主动退出;对于同等优先级,先运行的进程会一直占据cpu, 只有等到先运行的进程主动退出,后续进程才能得到时间片。
后者,高优先级会抢占低优先级,高优先级运行期间,低优先级没法抢占,只能等到高优先级主动退出, ;对于同等优先级的进程,各个进程会轮流运行一定的时间片(大约100ms)。

/*
 * ThreadTest.cpp
 *
 *  Created on: Jul 10, 2019
 *  Author: DY
 */
//#include "../core/Thread.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
using namespace std;
extern "C" void* Thread2 (void*);
extern "C" void* Thread1 (void*);
void* Thread1(void*)
{
    sleep(1);
    int i,j;
    int policy;
    struct sched_param param;
    pthread_getschedparam(pthread_self(),&policy,&param);
    if(policy == SCHED_OTHER)  printf("SCHED_OTHER\n");
    if(policy == SCHED_RR);    printf("SCHED_RR 1 \n");
    for(i=1;i<10;i++){
        for(j=1;j<500;j++){}
        printf("thread 1\n");}
}
void* Thread2(void*)
{
    sleep(1);
    int i,j;
    int policy;
    struct sched_param param;
    pthread_getschedparam(pthread_self(),&policy,&param);
    if(policy == SCHED_OTHER)  printf("SCHED_OTHER\n");
    if(policy == SCHED_RR);    printf("SCHED_RR\n");

    for(i=1;i<10;i++){
        for(j=1;j<500;j++){}
        printf("thread 2\n");}
}
int main(){
    pthread_t p1,p2;
    struct sched_param param;
    pthread_attr_t attr,attr1;

    //初始化
    pthread_attr_init(&attr);
    pthread_attr_init(&attr1);
    //设置线程模式
    pthread_attr_setschedpolicy(&attr,SCHED_RR);
    pthread_attr_setschedpolicy(&attr1,SCHED_RR);
    //获取线程属性并打印
    pthread_attr_getschedparam(&attr, &param);
    printf("%d\n",param.__sched_priority);
    pthread_attr_getschedparam(&attr1, &param);
    printf("%d\n",param.__sched_priority);

    //线程的优先级[1,99]值越大优先级越高
    param.sched_priority = 50;
    pthread_attr_setschedparam(&attr,&param);
    //该函数的意思为创建的进程优先级是否同父进程相同
    //pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED);
    //创建线程
    pthread_create(&p1,&attr,Thread1,NULL);
    pthread_attr_getschedparam(&attr, &param);
    printf("%d\n",param.__sched_priority);

    param.sched_priority = 20;
    pthread_attr_setschedparam(&attr1,&param);
    //pthread_attr_setinheritsched(&attr1,PTHREAD_EXPLICIT_SCHED);
    pthread_create(&p2,&attr1,Thread2,NULL);
    pthread_attr_getschedparam(&attr1, &param);
    printf("%d\n",param.__sched_priority);

    //等待线程退出并销毁
    pthread_join(p2,NULL);
    pthread_join(p1,NULL);
    pthread_attr_destroy(&attr);
    pthread_attr_destroy(&attr1);
    //pthread_exit(NULL);
    return 0;
}

程序运行过程中出现了问题 g++编译过程中会报如下错误: error: invalid conversion from ‘void (*)(void*)’ to ‘void* (*)(void*)’ [-fpermissive]。究其原因可能是C语言编译器允许隐含性的将一个通用指针转换为任意类型的指针,而在C++中不允许的。原因是pthread_create()中的第三个参数是载入一个函数,这个函数有一个参数可以传入,返回一个通用指针。
因此,出现上述错误的解决方法:1)线程函数定义为void  thread(void* ),而调用处写为:int ret = pthread_create(&id, &attr, (viod*)&thread, NULL);2)线程函数定义为void * thread1(void* ),调用处为:int ret = pthead_create(&id, &attr, thread1, NULL)。然后进行编译。


线程创建

//创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。
//函数原型:
int pthread_create(pthread_t *restrict tidp,
                    const pthread_attr_t *restrict attr,    
                    void *(*start_rtn)(void),void *restrict arg);
//形式参数:
//pthread_t *restrict tidp 要创建的线程的线程id指针,创建成功后,指向的内存存放创建线程的id;
//const pthread_attr_t *restrict attr一个不透明的属性对象,具体见上文属性的创建;
//void* (start_rtn)(void)返回值是void类型的指针函数;线程函数的起始地址,线程一旦创建立马执行;
//void* restrict arg start_rtn的形参;
//返回值:若是成功建立线程返回0,否则返回错误的编号。  
int ret = pthread_create (thread, attr, start_routine, arg);

线程挂起

函数原型:int pthread_join( pthread_t thread, void **value_ptr);

参数说明如下:thread等待退出线程的线程号;value_ptr退出线程的返回值。该函数的作用使得当前线程挂起,等待另一个线程返回才继续执行。也就是说当程序运行到这个地方时,程序会先停止,然后等线程id为thread的这个线程返回,然后程序才会断续执行。

线程终止

函数原型:int pthread_exit(void *rval_pt)

参数说明如下:

在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

猜你喜欢

转载自blog.csdn.net/Ding86341631/article/details/95244721