Linux c++ multithreading problem

Due to project issues, some thread-related knowledge has recently been involved, so let’s summarize everyone’s progress.

In the traditional Unix model, when a process needs to be executed by another entity, the process forks a child process and let the child process handle it. Most network server programs under Unix are written in this way, that is, the parent process accepts the connection, derives the child process, and the child process handles the interaction with the client.

Although this model has been used well for many years, there are some problems with forks:

  • Fork is expensive. The memory image should be copied from the parent process to the child process, and all description words should be copied in the child process and so on. At present, some Unix implementations use a technology called copy-on-write, which can avoid the copy-on-write of the parent process data space to the child process. Despite this optimization technique, fork is still expensive.
  • 2. After forking the child process, you need to use inter-process communication (IPC) to transfer information between the parent and child processes. The information before Fork is easy to pass, because the child process has a copy of the parent process data space and all description words from the beginning. But returning information from the child process to the parent process requires more work.

Threads help solve these two problems. Threads are sometimes called lightweight processes because they are "lightweight" than processes. Generally speaking, creating a thread is 10-100 times faster than creating a process.

    All threads in a process share the same global memory, which makes it easy for threads to share information, but this simplicity also brings synchronization problems.

All threads in a process not only share global variables, but also share: process instructions, most data, open files (such as description words), signal handlers and signal handling, current working directory, user ID and group ID. But each thread has its own thread ID, register set (including program counter and stack pointer), stack (used to store local variables and return addresses), error, signal mask, and priority. Thread programming in Linux conforms to the Posix.1 standard and is called Pthreads. All pthread functions start with pthread_. Before calling them, you must include the pthread.h header file, a function library libpthread.a.

Thread attributes


  • Structure and header file

The thread attribute structure is pthread_attr_t, which is defined in the header file /usr/include/pthread.h. The initialization function is pthread_attr_init, this function must be called before the pthread_create function. The attribute objects mainly include whether to bind, whether to separate, stack address, stack size, and priority. The default attributes are non-binding, non-separation, a default 1M stack, and the same priority as the parent process.

  • Thread binding

The function to set the thread binding status is pthread_attr_setscope. It has two parameters. The first is a pointer to the attribute structure, and the second is the binding type. It has two values: PTHREAD_SCOPE_SYSTEM (bound) and PTHREAD_SCOPE_PROCESS ( Unbound). The following code creates a bound thread.  

    Light Weight Process (LWP: Light Weight Process). A light process can be understood as a kernel thread, which is located between the user layer and the system layer. The allocation of thread resources and the control of threads by the system are realized by light processes, and one light process can control one or more threads. .     

  • Thread separation

The detached state of a thread determines how a thread terminates itself. The original thread waits for the end of the created thread. Only when the pthread_join() function returns, can the created thread be considered terminated and can release the system resources occupied by itself. The detached thread is not like this. It is not waited by other threads. When it runs over, the thread terminates and immediately releases system resources. The programmer should choose the appropriate separation state according to his needs. The function to set the thread detach state is pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate). The second parameter can be PTHREAD_CREATE_DETACHED (separated thread) and PTHREAD _CREATE_JOINABLE (non-separated thread

  • Thread priority

The priority setting is generally obtained first and then modified. To modify the thread priority, you need to pay attention to the type of process created.

SCHED_OTHER represents the normal process created.
SCHED_FIFO&&SCHED_RR represents the created thread whose priority can be modified, and the high priority will preempt the low priority. 
In the former, during high-priority operation, low-priority cannot be preempted and can only wait until the high-priority quits actively; for the same priority, the first running process will always occupy the cpu, and only after the first-running process quits actively can subsequent processes Get the time slice.
In the latter, high priority will preempt low priority. During high priority operation, low priority cannot preempt, and can only wait until high priority exits actively; for processes of the same priority, each process will run for a certain period of time in turn Slice (about 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;
}

There is a problem during program running. The following error will be reported during g++ compilation: error: invalid conversion from'void (*)(void*)' to'void* (*)(void*)' [-fpermissive]. The reason may be that the C language compiler allows implicit conversion of a general pointer to a pointer of any type, which is not allowed in C++. The reason is that the third parameter in pthread_create() is to load a function. This function has a parameter that can be passed in and returns a general pointer.
Therefore, the solution to the above error: 1) The thread function is defined as void thread(void*), and the calling place is written as: int ret = pthread_create(&id, &attr, (viod*)&thread, NULL); 2) Thread function Defined as void * thread1(void* ), the calling place is: int ret = pthead_create(&id, &attr, thread1, NULL). Then compile.


Thread creation

//创建线程成功时,函数返回 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);

Thread hang

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

The parameter description is as follows: thread number of the thread waiting to exit; the return value of value_ptr exiting the thread. The function of this function causes the current thread to suspend, waiting for another thread to return before continuing execution. That is to say, when the program runs to this place, the program will stop first, and then wait for the thread whose thread id is thread to return, and then the program will execute intermittently.

Thread termination

Function prototype: int pthread_exit(void *rval_pt)

The parameter description is as follows:

Here, it is pthread_exit used to explicitly exit a thread. Normally, the pthread_exit() function is called when the thread does not need to continue to exist after finishing its work. If main() ends before the thread it created and exits via pthread_exit(), then other threads will continue to execute. Otherwise, they will be automatically terminated at the end of main().

 

Guess you like

Origin blog.csdn.net/Ding86341631/article/details/95244721