0
点赞
收藏
分享

微信扫一扫

AVL树——自平衡的二叉搜索树

江南北 04-10 10:00 阅读 3

对页表的再次理解(以32位为例)

线程在进程内部执行,是OS调度的基本单位。

如何理解线程?

-------------------

上图中,每一个task_struct就是一个线程,红色方框内是属于一个进程。

创建线程,不用构建新的进程地址空间,页表,所以创建线程比创建进程更轻量化。

创建线程,只需要利用主线程(原进程)的地址空间,页表等资源。

在CPU看来,并不关心执行流是进程还是线程,只认task_struct,此时,可以说Linux没有真正意义上的线程结构,是用进程task_struct模拟线程的,Linux下的进程,统称为:轻量级进程!!!

-----------------------------------------------------------------------------------------------------------------------------

如何理解进程?

--------------------

用户视角:内核数据结构+对应的代码和数据!

内核视角:承担分配系统资源的实体!

----------------------------------------------------------------------------------------------------------------------------

线程的优点

线程的缺点

线程异常

因为创建的新线程,与主线程共用地址空间,页表等,所以新线程出现异常就会引起整个线程组异常。

线程用途

进程VS线程

进程是资源分配的基本单位;

线程是调度的基本单位;

线程共享进程数据,但也拥有自己的一部分数据:

关于主线程和新线程对栈的使用

主线程和子线程用的栈不在同一个区域,主线程用的栈就在地址空间的栈区,但是新线程用的栈区是在pthread库中对应的区域。

那么,怎么区分各个新线程的栈?

void *threadRun(void *args)
{
    const string name = (char *)args;
    while (true)
    {
        cout<<pthread_self()<<endl;
        sleep(1);
    }
}

int main()
{
    pthread_t tid;
    pthread_create(&tid, nullptr, threadRun, (void *)"new thread");

    while (true)
    {
        cout << "main thread, pid: " << getpid() << endl;
        sleep(1);
    }
}

输出结果: 

我们发现,新线程的ID值非常大,我们在哪里见到过这么大的值那?--------虚拟地址!!!

其实不然,新线程的ID值,就是用来标识该线程在pthread库中对应的存储信息的起始位置,可以结合前边的图看到。

进程的多个线程共享 同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

主线程与新线程共用全局变量

void *threadRoutine(void *args)
{
    pthread_detach(pthread_self());

    while(true)
    {
        cout << (char*)args << " : " << g_val << " &: " << &g_val << endl;
        g_val++;
        sleep(1);
    }
    pthread_exit((void*)11);
}

int main()
{
    pthread_t tid; 
    pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");

    while(true)
    {
        cout << "main thread" << " : " << g_val << " &: " << &g_val << endl;
        sleep(1);
    }
    return 0;
}

部分打印结果: (我们发现,新线程修改全局变量,主线程的也会改变,而且地址是一样的!)

 

如果用  __thread  来修饰全局变量的话,会发现新线程修改全局变量,主线程看到的值并不会发生改变,而且二者对应的地址不同。

进程ID和线程ID

 认识进程ID和线程ID

void *threadRun(void *args)
{
    const string name = (char *)args;
    while (true)
    {}
}

int main()
{
    pthread_t tid[5];
    for (int i = 0; i < 5; i++)
    {
        pthread_create(tid + i, nullptr, threadRun, (void *)"");
    }

    while (true)
    {}
}

ps 命令中的 -L 选项,会显示如下信息:

 其中,PID值和LWP值相同的是主线程,其余的都是新线程。

线程库 NPTL 提供了 pthread_ self 函数,可以获得线程自身的 ID

线程控制

POSIX线程库
创建线程
线程终止

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

 pthread_exit函数

pthread_cancel函数

线程等待

调用该函数的线程将挂起等待 , 直到 id thread 的线程终止。 thread 线程以不同的方法终止 , 通过 pthread_join 得到的终止状态是不同的,总结如下:

为什么需要线程等待?

 分离线程

可以是线程组内其他线程对目标线程进行分离,也可以是线程自己分离 :

关于新线程退出结果
void *threadRoutine(void *args)
{
    while(true)
    {
        pthread_testcancel();
    }
}

int main()
{
    pthread_t tid; 
    pthread_create(&tid, nullptr, threadRoutine, (void *)"thread 1");
    int count = 0;
    while (true)
    {
        count++;
        if (count >= 5)
            break;
    }
    int n= pthread_cancel(tid);
    cout <<n<<endl;
    cout << "pthread cancel: " << tid << endl;

    int *ret = nullptr;
    pthread_join(tid, (void **)&ret); // 默认会阻塞等待新线程退出

    cout << "main thread wait done ... main quit ...: new thead quit : " << (long long)ret << "\n";

    return 0;
}

如果调用pthread_cancel来终止线程,则线程的退出码是 -1。

当然,如果新线程指定返回退出结果,可通过(void*)进行强转,然后主线程再(long long)进行强转,就可获得。

举报

相关推荐

0 条评论