【Linux操作系统】线程控制

文章目录

  • 线程创建
  • 线程等待
  • 终止线程
  • 利用多线程求和(单进程多线程)
  • 获取线程ID
  • 取消线程
  • 线程分离
  • 共享?


线程创建

创建线程需要用的函数是pthread_create。函数原型如下:

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
 void *(*start_routine) (void *), void *arg);

参数说明

  • thread:获取创建成功的线程ID,该参数是一个输出型参数。
  • attr:用于创建线程的属性,传入NULL表示使用默认属性。
  • start_routine:该参数是一个函数地址,表示线程例程,即线程启动后要执行的函数。
  • arg:传给线程例程的参数。

创建线程代码示例:以下有一个主线程,并创建了一个新线程,主线程打印了pthread_create函数的第一个输出型参数,即新线程的id。
【Linux操作系统】线程控制_第1张图片
运行结果
【Linux操作系统】线程控制_第2张图片

演示:以下代码有一个主线程,利用snprintf对每个新线程进行了命名,并通过pthread_create函数创建了10个新线程,同时给thread_run函数传了参数tname。

【Linux操作系统】线程控制_第3张图片
运行结果:

【Linux操作系统】线程控制_第4张图片

线程等待

线程和进程一样,也是需要等待的,否则就会类似产生“僵尸进程”,产生内存泄漏的问题。等待线程用的是pthread_join函数,函数原型如下:

int pthread_join(pthread_t thread, void **retval);

参数说明

  • thread:被等待线程的ID。
  • retval:线程退出时的退出码信息。
  • 线程等待成功返回0,失败返回错误码。

注意:pthread_join属于阻塞等待。

代码示例

【Linux操作系统】线程控制_第5张图片
【Linux操作系统】线程控制_第6张图片
【Linux操作系统】线程控制_第7张图片

终止线程

如果需要只终止某个线程而不是终止整个进程,有三种方法:

  • 从线程函数return。
  • 线程可以调用pthread_exit函数终止自己。
  • 一个线程可以调用pthread_cancel函数终止同一进程中的另一个线程。

【Linux操作系统】线程控制_第8张图片
【Linux操作系统】线程控制_第9张图片

【Linux操作系统】线程控制_第10张图片

利用多线程求和(单进程多线程)

  1 #include<iostream>
  2 #include<string>
  3 #include<unistd.h>
  4 #include<pthread.h>
  5 #include<ctime>
  6 
  7 #define NUM 10
  8 
  9 using namespace std;
 10 
 11 enum
 12 {
 13     OK=0,
 14     ERROR
 15 };
 16 
 17 class ThreadData
 18 {
 19 public:
 20     ThreadData(const string& name,int id,time_t createTime,int top)
 21     :_name(name)
 22     ,_id(id)
 23     ,_createTime((uint64_t)createTime)
 24     ,_status(OK)
 25     ,_top(top)
 26     ,_result(0)
 27     {}
 28     ~ThreadData()
 29     {}
 30 public:
 31     //输入的
 32     string _name;//线程名
 33     int _id;//线程id
 34     uint64_t _createTime;//线程创建时间
 35 
 36     //返回的
 37     int _status;//线程状态
 38     int _top;//求和至哪个整数
 39     int _result;//返回求和结果
 40 };
 41 void *thread_run(void *args)                                                                                                                                                     
 42 {
 43     ThreadData *td = static_cast<ThreadData *>(args);
 44 
 45     for(int i = 1; i <= td->_top; i++)
 46     {
 47         td->_result += i;
 48     }
 49     cout << td->_name << " cal done!" << endl;
 50     return td;
 51                                                                                                                                                                                  
 52     // while (true)
 53     // {
 54     //     cout << "thread is running, name " << td->_name << " create time: " << td->_createTime << " index: " << td->_id << endl;
 55     //     // // exit(10); // 进程退出,不是线程退出,只要有任何一个线程调用exit,整个进程(所有线程)全部退出!
 56     //     // sleep(4);
 57         
 58     //     // break;
 59     // }
 60     // delete td;
 61 
 62     // pthread_exit((void*)2);   // void *ret = (void*)1;
 63     // return nullptr;
 64 }
 65 int main()
 66 {
 67 
 68     //1.创建线程
 69     pthread_t tids[NUM];
 70 
 71     for(int i=0;i<NUM;i++)
 72     {
 73         char tname[64];
 74         //1.1将字符串格式化进tname
 75         snprintf(tname,64,"thread-%d",i+1);
 76         //1.2创建类对象:线程名,线程id,线程创建的时间戳,top表示求和到哪个整数
 77         ThreadData* td = new ThreadData(tname,i+1,time(nullptr),100+5*i);
 78 
 79         //1.3
 80         //thread:获取创建成功的线程ID,该参数是一个输出型参数。
 81         //创建线程的属性,传入NULL表示使用默认属性
 82         //线程启动后要执行的函数,这里用的是thread_run函数
 83         //传给线程例程的参数,我们自定义了一个求和整数的类对象
 84         pthread_create(tids+i,nullptr,thread_run,td);//创建线程,td是thread_run函数的参数,
 85         //同时td也是我们自己创建的类对象
 86         sleep(1);
 87     }
 88 
 89     //2.等待线程
 90     void *ret = nullptr;
 91     for(int i=0;i<NUM;i++)
 92     {
 93         //int pthread_join(pthread_t thread, void **retval);,注意是void**类型的
 94         //被等待线程的id,获取线程退出码
 95         int n = pthread_join(tids[i],&ret);
 96 
 97         if(n!=0)
 98         {
 99             cerr << "pthread_join error" << endl;
100         }
101         //获取线程退出码,将ret强制转换成ThreadData*
102         ThreadData* td = static_cast<ThreadData*>(ret);
103 
104         if(td->_status == OK)
105         {
106             cout << td->_name << " 计算的结果是: " << td->_result << " (它要计算的是[1, " << td->_top << "])" <<endl;
107         }
108         delete td;
109     }
110     cout << "all thread quit..." << endl;
111     return 0;
112 }

运行结果:
【Linux操作系统】线程控制_第11张图片

获取线程ID

常见获取线程ID的方式有两种:

  1. 创建线程时通过输出型参数获得。
  2. 通过调用pthread_self函数获得。调用pthread_self函数即可获得当前线程的ID,类似于调用getpid函数获取当前进程的ID。

pthread_self函数原型如下:

pthread_t pthread_self(void);

取消线程

pthread_cancel函数可以取消线程,pthread_cancel函数的函数原型如下:

int pthread_cancel(pthread_t thread);//thread是被取消线程的ID。

代码实例:

#include
#include
#include
#include
#include

using namespace std;

void* thread_run(void* args)
{
    const char* name = static_cast<const char*>(args);

    int cnt=5;
    while(cnt)
    {
        cout<<name<<"is running"<<cnt--<<"获取该线程id"<<pthread_self()<<endl;
        sleep(1);
    }
    //5s后就终止线程
    pthread_exit((void*)11);
}

int main()
{
    pthread_t tid;
    //创建单进程单线程
    pthread_create(&tid,nullptr,thread_run,(void*)"thread1");

    sleep(3);//睡眠3s就取消线程

    pthread_cancel(tid);

    void* ret=nullptr;
    //等待线程
    pthread_join(tid,&ret);
    cout<<"new thread exit:"<<(int64_t)ret<<"退出线程"<<tid<<endl;
    return 0;
}

在这里插入图片描述
线程是可以取消自己的,取消成功的线程的退出码一般是-1。

线程分离

线程分离的函数:

int pthread_detach(pthread_t thread);//thread是所要分离线程的ID。
  • 默认情况下,新创建的线程是joinable(需要主线程等待回收资源的)的,线程退出后,需要对其进行pthread_join操作,否则无法释放资源,从而造成内存泄漏。
  • 但如果我们不关心线程的返回值,join也是一种负担,此时我们可以将该线程进行分离,后续当线程退出时就会自动释放线程资源。这里的意思是不再需要主线程去join了,当这个线程退出时系统会自动回收该线程所对应的资源。
  • 可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离。
  • 线程分离之后,就不能join了,不然会报错。
  • joinable和分离是冲突的,一个线程不能既是joinable又是分离的。

代码示例
创建五个新线程后让这五个新线程将自己进行分离,主线程就不需要在对这五个新线程进行join了,当这5个线程退出时系统会自动回收该线程所对应的资源。

#include 
#include 
#include 
#include 
#include 

void* Routine(void* arg)
{
	pthread_detach(pthread_self());
	char* msg = (char*)arg;
	int count = 0;
	while (count < 5){
		printf("I am %s...pid: %d, ppid: %d, tid: %lu\n", msg, getpid(), getppid(), pthread_self());
		sleep(1);
		count++;
	}
	pthread_exit((void*)6666);
}
int main()
{
	pthread_t tid[5];
	for (int i = 0; i < 5; i++){
		char* buffer = (char*)malloc(64);
		sprintf(buffer, "thread %d", i);
		pthread_create(&tid[i], NULL, Routine, buffer);
		printf("%s tid is %lu\n", buffer, tid[i]);
	}
	while (1){
		printf("I am main thread...pid: %d, ppid: %d, tid: %lu\n", getpid(), getppid(), pthread_self());
		sleep(1);
	}
	return 0;
}

共享?

  • 全局变量是所有线程共享的,如果不想它被所以线程共享,需要加上__thread修饰变量。
  • 线程中的局部变量不是所有线程共享的。

同一线程内的局部变量地址是一致的,不同线程的局部变量是不共享的。

代码示例:

#include
#include
#include
#include

using namespace std;


void *threadRoutine(void* args)
{
    string name = static_cast<const char*>(args);
    int cnt = 5;
    while(cnt)
    {
        cout << name << " : " << cnt-- << " : " << pthread_self() << " &cnt: " << &cnt << endl;
        //cout << name << " g_val: " << g_val++ << ", &g_val: " << &g_val << endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    //线程创建
    pthread_t t1,t2,t3;
    pthread_create(&t1,nullptr,threadRoutine,(void*)"thread 1");
    pthread_create(&t2,nullptr,threadRoutine,(void*)"thread 2");
    pthread_create(&t3,nullptr,threadRoutine,(void*)"thread 3");

    //线程等待
    pthread_join(t1,nullptr);
    pthread_join(t2,nullptr);
    pthread_join(t3,nullptr);

    return 0;
}

【Linux操作系统】线程控制_第12张图片

一个进程内的全局变量是所有线程共享的。

#include
#include
#include
#include

using namespace std;

int g_val = 100;
// std::string hexAddr(pthread_t tid)
// {
//     g_val++;
//     char buffer[64];
//     snprintf(buffer, sizeof(buffer), "0x%x", tid);

//     return buffer;
// }
void *threadRoutine(void* args)
{
    string name = static_cast<const char*>(args);
    int cnt = 5;
    while(cnt)
    {
        //cout << name << " : " << cnt-- << " : " << pthread_self() << " &cnt: " << &cnt << endl;
        cout << name << " g_val: " << g_val++ << ", &g_val: " << &g_val << endl;
        sleep(1);
    }
    return nullptr;
}

int main()
{
    pthread_t t1, t2, t3;
    pthread_create(&t1, nullptr, threadRoutine, (void*)"thread 1"); // 线程被创建的时候,谁先执行不确定!
    pthread_create(&t2, nullptr, threadRoutine, (void*)"thread 2"); // 线程被创建的时候,谁先执行不确定!
    pthread_create(&t3, nullptr, threadRoutine, (void*)"thread 3"); // 线程被创建的时候,谁先执行不确定!

    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);
    pthread_join(t3, nullptr);

   
    return 0;
}

【Linux操作系统】线程控制_第13张图片

用__thread修饰全局变量,这个全局变量就不会被所以线程共享了。

【Linux操作系统】线程控制_第14张图片

注意:我们在执行多线程打印的时候,可能打印出来的结果会出现错乱,这是因为多线程打印都要访问显示器文件,显示器也是一个文件,文件自然是共享资源,所以当多个线程访问同一个共享资源的时候,会出现调度问题,导致打印错乱,这个问题属于线程互斥的内容,需要加锁处理,下一篇文章我们再来详谈线程互斥问题。

你可能感兴趣的:(Linux操作系统,java,数据库,android)