[Linux] 스레드 제어

이전 블로그에서 쓰레드의 개념 으로 쓰레드의 장점이 많지만 합리적인 접근 제어가 부족하니 쓰레드 제어 방법을 살펴 보겠습니다 ~

POSIX 스레드 라이브러리

Linux에는 실제 스레드가 없다는 것을 알고 있습니다. CPU가 보는 것은 프로세스 엔티티 일 뿐이므로 스레드는 무 (無)가됩니다. 즉, Linux는 스레드를 관리하기위한 일련의 시스템 호출을 제공하지 않습니다. 운영 체제는 구조를 제공하지 않기 때문에 스레드를 관리하기위한 인터페이스 세트를 구현합니다.

POSIX는 이식 가능한 운영 체제 인터페이스를 의미합니다. 여기서 소개하는 함수는 POSIX 표준도 따릅니다. 이러한 함수는 일련의 사용자 수준 호출이며 관련 함수가있는 완전한 시리즈를 구성합니다. 대부분의 이름은 "pthread_"로 시작합니다. , 그리고 함수 라이브러리를 사용하여, 우리는 <pthread.h를> 헤더 파일을 소개해야하고, 가장 중요한 점은 여기가 우리가이 스레드 함수 라이브러리를 링크 할 때 컴파일 할 때 "-pthread"옵션을 추가해야한다는 것입니다 및 -l 링크 제 3 자 라이브러리를 의미합니다.
여기에 사진 설명 삽입

1. 스레드 생성

여기에 사진 설명 삽입
매개 변수 1 : 출력 매개 변수 인 스레드의 ID를 반환합니다.
매개 변수 2 : 기본 속성을 나타내는 nullptr을 사용하여 스레드의 속성을 설정합니다.
매개 변수 3 : 하위 스레드가 함수의 주소 인 함수 이름을 실행하도록합니다.
매개 변수 4 : 자식 스레드가 함수를 실행합니다
반환 값 의 매개 변수 : 성공은 0을 반환하고 실패는 오류 코드를 반환합니다.
시도 할 스레드를 만들어 보겠습니다.

#include <iostream>
#include <pthread.h>
#include <unistd.h>

using namespace std;

void *handle(void *arg)
{
	while(1)
	{
		cout<<"i am new thread"<<endl;
		sleep(1);
	}
}
int main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,handle,(void*)"thread 1");
	while(1)
	{
		cout<<"i am main thread"<<endl;
		sleep(2);
	}
	return 0;
}

여기에 사진 설명 삽입
우리가 만든 스레드를 확인하십시오.

ps -ajL
-L 옵션은 스레드 관련 정보를 표시 할 수 있습니다.

여기에 사진 설명 삽입

  • 위의 그림에서 동일한 PID가 8974 인 두 개의 프로세스가 있음을 알 수 있습니다. 이는이 두 프로세스가 실제 프로세스가 아님을 의미합니다. 둘의 LWP (Lightweighted Process)가 다르며 이는 서로 다른 경량 프로세스 또는 스레드임을 나타냅니다. 실제로 작업을 예약하는 CPU 단위는 lwp 이고 앞서 언급 한 프로세스의 예약 단위는 단일 스레드 프로세스의 PID입니다.
  • 각 사용자 모드 스레드는 커널의 스케줄링 엔티티에 해당합니다. 따라서 OS는 스레드를 관리하기 위해 스레드를 설명하고 구성해야하므로 스레드는 자체 프로세스 설명자 (task_struct 구조), 스레드 로컬 저장소 및 스레드 스택도 갖습니다. , 구조의 시작 주소는 스레드 구조를 설명하는 특정 주소를 찾는 것이므로 스레드를 고유하게 식별하는 데 사용되는 정수 변수 tid는 이러한 스레드 설명의 첫 번째 주소이고 LWP는 스케줄링에 사용되는 ID입니다.
    여기에 사진 설명 삽입
  • 노트: 스레드와 프로세스가 다르고 프로세스는 부모 프로세스의 개념을 가지고 있습니다. 그러나 스레드 그룹에서 모든 스레드는 피어입니다. 즉, 동일한 프로세스의 스레드를 스레드 그룹이라고하며 계층 관계가 없으며 주 스레드와 새 스레드 만 있습니다.

스레드 종료

프로세스를 종료하지 않고 스레드를 종료하려면 다음 세 가지 방법이 있습니다.

  1. 스레드 함수에서 반환합니다. 주 스레드 반환의 경우 주 함수에 대한 종료를 호출하는 것과 같습니다.
  2. 스레드는 pthread_exit를 호출하여 스스로 종료 할 수 있습니다.

여기에 사진 설명 삽입

  1. 스레드는 pthread_cancel 함수를 호출하여 스레드 그룹의 다른 스레드를 종료 할 수 있습니다. 일반적으로 주 스레드가 다른 새 스레드를 취소하는 시나리오에서 사용됩니다.

여기에 사진 설명 삽입
여기서 한 가지 세부 사항은 기본 스레드에서 자식 스레드를 취소 할 때 기본 스레드가 예약 된 후 자식 스레드가 취소되었는지 확인해야한다는 것입니다. 그렇지 않으면 올바른 결과를 얻을 수 없습니다. 자식 스레드가 올바르게 취소되면 조인 함수의 포인터가 -1을 가리 킵니다.

#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;
void* handle(void *arg)
{
	int i=3;
	while(i--)
	{
		cout<<"i am new"<<endl;
	}
	pthread_exit((void*)2);
}
int main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,handle,(void*)"thread 1");
	void* status;
	pthread_join(tid,&status);
	cout<<(int*)status<<endl;
	return 0;
}

여기에 사진 설명 삽입

스레드 대기

  1. 메인 스레드가 대기중인 이유 :

메모리 누수를 방지하고
주 스레드가 새 스레드의 종료 상태를 얻고
스레드 종료 시퀀스의 동기화를 보장합니다.

  1. 대기 기능

여기에 사진 설명 삽입
첫 번째 매개 변수는 대기중인 스레드의 ID이고 두 번째 매개 변수는 스레드의 반환 값을 가리키는 포인터를 가리 킵니다.
함수 호출이 성공하면 0을 반환하고 실패하면 오류 코드를 반환합니다.

  1. 이 함수를 호출하는 스레드는 일시 중단되고 ID가 스레드 인 스레드가 종료 될 때까지 대기합니다. 따라서 스레드 종료 방법이 다를 경우 조인으로 얻은 종료 상태가 다릅니다.

다음은 스레드 종료에 대한 작은 세부 사항입니다. join 함수의 두 번째 매개 변수는 스레드 종료의 종료 코드를 가져 오는 것입니다. 이는 프로세스가 대기 중일 때와 달리 종료 코드 일뿐입니다. 스레드가 프로세스의 A 분기이기 때문에 스레드가 비정상적인 한 프로세스가 종료되므로 예외 코드를 얻을 수없고 종료 코드
1 만 얻을 수 있습니다 . 스레드 스레드가 반환을 통해 반환되면 retval이 가리키는 단위는 함수의 반환 값을 저장합니다.
2. 다른 스레드가 pthread_cancel 함수를 호출하여 스레드 스레드가 비정상적으로 종료되면 상수 PTHREADCANLELED가 retval이 가리키는 단위에 저장됩니다.
3. 스레드 스레드가 종료하기 위해 pthread_exit 함수를 호출하면 retval이 가리키는 단위는 종료 함수에 전달 된 매개 변수를 저장합니다.
4. 스레드의 종료 상태에 관심이 없으면 NUL을 두 번째 매개 변수에 직접 전달하십시오.

  1. 테스트 코드
#include <iostream>
#include <pthread.h>
#include <unistd.h>

using namespace std;
void* handle(void* arg)
{
	int i=3;
	while(i--)
		cout<<"i am new"<<endl;
	return (void*)1;
}

int main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,handle,(void*)"thread 1");
	void* status;
	pthread_join(tid,&status);
	cout<<(int*)status<<endl;
	return 0;
}

여기에 사진 설명 삽입

스레드 ID

  • 스레드 ID를 얻는 방법

여기에 사진 설명 삽입

  • 스레드 이전에 프로세스는 커널의 프로세스 설명자에 해당하고 프로세스 ID에 해당합니다. 그러나 스레드 개념이 도입 된 후 상황이 변경되었습니다. 사용자 프로세스는 N 개의 사용자 모드 스레드를 관리합니다. 독립적 인 스케줄링 엔티티로서 각 스레드는 커널 모드에서 자체 프로세스 설명자 , 프로세스 및 커널의 설명자를 갖습니다. 일대일 관계가 됩니다 .POSIX 표준은 프로세스의 모든 스레드가 getpid 함수를 호출 할 때 동일한 프로세스 ID를 반환하도록 요구합니다. 위의 문제를 해결하는 방법은 무엇입니까?
  • Linux 커널에서는 다중 스레드 프로세스를 스레드 그룹이라고하며 스레드 그룹의 각 스레드에는 커널에 해당하는 프로세스 설명자가 있습니다. 프로세스 디스크립터 구조의 pid는 표면적으로 프로세스 ID에 해당하지만 실제로는 그렇지 않고 첫 번째 스레드 인 메인 스레드의 ID에 해당합니다. 프로세스 설명의 tgid는 스레드 그룹 ID를 의미합니다. 사용자 수준 프로세스 ID 값에 해당합니다.
  • 따라서 실제로 보는 프로세스 pid는 test_struct의 tgid에 해당하고 lwp는 task_struct의 pid에 해당합니다. 그런 다음 task_struct의 구조를 살펴 봅니다.
 struct task_struct {
...
pid_t pid;
pid_t tgid;
...
struct task_struct *group_leader;//主线程
...
struct list_head thread_group;//用来描述一个线程组的链表
...
};

다음에 사용자 모드에서 gitpid를 호출하면 시스템이 실제로 test_struct의 tgid를 반환했음을 알게됩니다. Gittid는 또한 스레드 ID를 반환하기 위해 Linux에서 제공되지만이 시스템 호출은 캡슐화되지 않으며 사용하기 매우 편리하지 않습니다.

스레드 분리

기본적으로 새로 생성 된 스레드는 조인 할 수 있습니다. 스레드가 종료 된 후 해당 스레드에 대해 pthread_join 작업을 수행해야합니다. 그렇지 않으면 리소스를 해제 할 수 없어 메모리 누수가 발생합니다. 반환 값에 대해 신경 쓰지 않는 경우 스레드, 조인은 부담이되므로 자식 스레드를 분리하고 스레드 리소스를 자동으로 해제하도록 선택할 수 있습니다. 스레드는 스스로 분리되거나 다른 스레드가 분리되도록 할 수 있습니다.
여기에 사진 설명 삽입
노트:스레드는 분리 된 후 기다릴 수 없습니다.

#include <iostream>
#include <pthread.h>
#include <unistd.h>

using namespace std;
void* handle(void* arg)
{
	pthread_detach(pthread_self());
	int i=3;
	while(i--)
		cout<<"i am new"<<endl;
	pthread_cancel(pthread_self());
	return (void*)1;
}

int main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,handle,(void*)"thread 1");
	void* status;
	int ret=pthread_join(tid,&status);
	cout<<(int*)status<<":"<<ret<<endl;
	return 0;
}

여기에 사진 설명 삽입

요약하자면 :

Linux에서 사용되는 스레드 제어 함수는 실제로 사용자 수준의 함수 그룹이며 이러한 함수의 사용을 마스터 할 수있는 한 스레드를 제어하는 ​​것은 매우 간단합니다.

추천

출처blog.csdn.net/ly_6699/article/details/97961108