【linux学习】多线程(1)

【linux学习】多线程(1)

码农世界 2024-05-19 后端 65 次浏览 0个评论

文章目录

  • 线程的概念
    • 线程与进程
    • 线程的用法
      • 线程的创建
        • 多线程
        • 线程的等待
        • 线程锁
          • 死锁

            线程的概念

            在Linux中,线程(Thread)是程序执行流的最小单位,是进程中的一个实体,负责在程序中执行代码。线程本身不拥有系统资源,但它可以访问其所属进程的资源,包括内存空间、文件句柄等。线程与进程的主要区别在于线程是共享进程资源的,而进程之间则是独立的。

            线程与进程

            在Linux中,线程有时被称为轻量级进程(Lightweight Process, LWP)。从内核的角度来看,线程和进程在很大程度上是相似的,它们都通过task_struct结构体来描述。但是,线程与父进程(或其他线程)共享某些资源,如地址空间和文件描述符表,而进程则拥有自己的独立资源。

            【linux学习】多线程(1)

            PCB(Process Control Block)即进程控制块。

            虽然线程和进程都使用task_struct来描述,但Linux内核通过一些特定的字段来区分它们。例如,每个task_struct都有一个pid(进程ID)和一个tgid(线程组ID)。对于主线程(也就是我们通常所说的“进程”),其pid和tgid是相同的。但是,对于该进程创建的其他线程,它们的pid是唯一的,但tgid与主线程的tgid(也就是该进程的ID)相同。通过这种方式,内核可以区分一个task_struct描述的是进程还是线程。

            进程与线程关系图:

            (波浪线代表一个线程)

            【linux学习】多线程(1)

            线程的用法

            线程的创建

            需要pthread_creart函数来创建线程。

            【linux学习】多线程(1)

            #include 
            #include 
            #include 
            void *ThreadRoutine(void *args)
            {
                const char *threadname = (const char *)args;
                while (true)
                {
                    std::cout << "i am a new: " << threadname << std::endl;
                    sleep(1);
                }
            }
            int main()
            {
                pthread_t tid;
                pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread 1");
                while(true)
                {
                    std:: cout << "i am main thread" << std::endl;
                    sleep(1);
                }
                return 0;
            }
            

            【linux学习】多线程(1)

            看图可以得知我们创建了一个线程,主线程仍然在继续运行。

            多线程

            void *ThreadRoutine(void *args)
            {
                const char *threadname = (const char *)args;
                while (true)
                {
                    std::cout << "i am a new: " << threadname << std::endl;
                    sleep(3);
                }
            }
            int main()
            {
                pthread_t tid1;
                pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");
                sleep(3);
                pthread_t tid2;
                pthread_create(&tid2, nullptr, ThreadRoutine, (void *)"thread 2");
                sleep(3);
                pthread_t tid3;
                pthread_create(&tid3, nullptr, ThreadRoutine, (void *)"thread 3");
                sleep(3);
                pthread_t tid4;
                pthread_create(&tid4, nullptr, ThreadRoutine, (void *)"thread 4");
                sleep(3);
                while (true)
                {
                    std::cout << "i am main thread" << std::endl;
                    sleep(3);
                }
                return 0;
            }
            

            可以看出线程2,3,4都正常运行。

            【linux学习】多线程(1)

            线程的等待

            【linux学习】多线程(1)

            pthread_t thread:这是你想要等待的线程的标识符。这个标识符是在调用pthread_create时返回的。

            **void retval:这是一个指向指针的指针,用于获取被等待线程的返回值。如果retval不是NULL,那么pthread_join会将终止线程的返回值放在*retval所指向的位置。如果你对线程的返回值不感兴趣,可以将这个参数设置为NULL。

            #include 
            #include 
            #include 
            void *ThreadRoutine(void *args)
            {
                int cnt = 5;
                const char *threadname = (const char *)args;
                while (cnt)
                {
                    std::cout << "i am a new: " << threadname << std::endl;
                    sleep(1);
                    cnt--;
                }
                return args;
            }
            int main()
            {
                pthread_t tid1;
                pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");
                void *ret = nullptr;
                pthread_join(tid1,&ret);
                std:: cout << (char *)ret << std::endl;
                sleep(3);
                //while ()
                //{
                    std::cout << "i am main thread" << std::endl;
                    sleep(1);
                //}
                return 0;
            }
            

            【linux学习】多线程(1)

            在函数体内,我们将args作为返回值,可以看到将函数的返回值通过pthread_join()函数传送到ret这个指针上.

            线程锁

            线程锁(Thread Lock)是一种同步机制,主要用于解决多线程访问共享资源时可能出现的并发问题。

            #include 
            #include 
            #include 
            int ticket = 10000;
            void *ThreadRoutine(void *args)
            {
                const char *threadname = (const char *)args;
                while (ticket > 0)
                {
                    std::cout << "i am : " << threadname << "getticket:" << ticket-- << std::endl;
                }
                return args;
            }
            int main()
            {
                pthread_t tid1;
                pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");
                pthread_t tid2;
                pthread_create(&tid2, nullptr, ThreadRoutine, (void *)"thread 2");
                pthread_t tid3;
                pthread_create(&tid3, nullptr, ThreadRoutine, (void *)"thread 3");
                pthread_t tid4;
                pthread_create(&tid4, nullptr, ThreadRoutine, (void *)"thread 4");
                void *ret1 = nullptr;
                void *ret2 = nullptr;
                void *ret3 = nullptr;
                void *ret4 = nullptr;
                pthread_join(tid1, &ret1);
                pthread_join(tid2, &ret2);
                pthread_join(tid3, &ret3);
                pthread_join(tid4, &ret4);
                std::cout << "i am main thread,Finish" << std::endl;
                return 0;
            }
            

            如以上代码,我们模拟一个多个线程抢车票的的进程。代码中设计当车票为零时,则退出while循环,并且退出函数,退出线程。

            但经过我们多次试验发现图下现象:

            【linux学习】多线程(1)

            票数竟会变成负数?

            假设ticket值为1当一个线程已经进入while循环内,但对于ticket值并没有做出改变,此时另一个线程就会用相同的ticket值也进入了while循环,两个进程又都进行了减减操作,导致ticket值变为-1.

            这时就要用上锁,来保证多线程防止同时访问共享资源。

            代码实现:

            #include 
            #include 
            #include 
            int ticket = 10000;
            pthread_mutex_t lock;//全局锁
            void *ThreadRoutine(void *args)
            {
                const char *threadname = (const char *)args;
                pthread_mutex_lock(&lock);//上锁
                while (ticket > 0)
                {
                    std::cout << "i am : " << threadname << "getticket:" << ticket-- << std::endl;
                }
                pthread_mutex_unlock(&lock);//解锁
                return args;
            }
            int main()
            {
                pthread_mutex_init(&lock, nullptr);
                pthread_t tid1;
                pthread_create(&tid1, nullptr, ThreadRoutine, (void *)"thread 1");
                pthread_t tid2;
                pthread_create(&tid2, nullptr, ThreadRoutine, (void *)"thread 2");
                pthread_t tid3;
                pthread_create(&tid3, nullptr, ThreadRoutine, (void *)"thread 3");
                pthread_t tid4;
                pthread_create(&tid4, nullptr, ThreadRoutine, (void *)"thread 4");
                void *ret1 = nullptr;
                void *ret2 = nullptr;
                void *ret3 = nullptr;
                void *ret4 = nullptr;
                pthread_join(tid1, &ret1);
                pthread_join(tid2, &ret2);
                pthread_join(tid3, &ret3);
                pthread_join(tid4, &ret4);
                std::cout << "i am main thread,Finish" << std::endl;
                return 0;
            }
            

            通过给区间上锁,来防止多线程同时访问资源。

            死锁

            【linux学习】多线程(1)

            如上图,线程A,线程B,倘若线程A中申请到了LOCK1,而线程B中申请到了LOCK2,线程A等待LOCK2的释放,线程B等待LOCK1的释放,就会导致互相等待,两个线程都进行不下去,导致死锁

            如何避免:

            避免嵌套锁:尽量不在持有锁的同时请求另一个锁。如果必须这样做,确保加锁的顺序在所有线程中都是一致的。

            保持锁的顺序一致:多个线程在尝试获取多个锁时,总是以相同的顺序请求它们。这可以防止循环等待条件的发生,即线程A等待线程B释放锁,而线程B又在等待线程A释放另一个锁。

            使用超时机制:在尝试获取锁时设置超时。如果线程不能在规定的时间内获得锁,它将放弃并稍后重试。这可以防止线程无限期地等待,从而增加了系统的灵活性。

            减少锁的粒度:尽量只锁定需要保护的最小资源范围。例如,如果你可以只锁定一个数据结构的一部分而不是整个数据结构,那么这将减少死锁的可能性。

            避免长时间持有锁:尽量缩短持有锁的时间。这意味着你应该在获得锁后尽快完成你的工作并释放锁。

转载请注明来自码农世界,本文标题:《【linux学习】多线程(1)》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,65人围观)参与讨论

还没有评论,来说两句吧...

Top