zoukankan      html  css  js  c++  java
  • 多线程01

    一、并行和并发:

    并行:多个任务在同一时刻同时执行。

    并发:多个任务顺序执行,不是同时。

    二、进程和线程:

    进程是系统分配资源的最小单位,线程是cpu执行任务的最小单位。

    操作系统中,每个进程都有自己的地址空间和一个执行线程,这个线程通常叫做主线程

    对于单核CPU而言,同一时刻只能执行一个线程。每隔一定时间会切换线程(可能是同一个进程的线程,也可能是另外一个进程的线程,如果是其它进程的线程)。

    在单核CPU上实现的多线程其实是“假”的多线程,CPU同一时刻只能执行一个线程,只不过CPU切换任务的速度很快,所以你感觉是同时执行了多个任务,即实现了多线程。

    在单核CPU上,无论是线程还是进程,都只能并发执行,多核CUP上有可能实现并行执行。

    三、多线程

    下面用一个最简单的多线程例子说明。

    C++11之前创建多线程的函数有2个:_beginthreadex、CreateThread

    前者是C++库函数,后者是Windows API。我们使用_beginthreadex。

    int main()
    {
        for (int i = 0; i < 5; i++)
        {
            cout<<"1234567890------"<< i<<endl;
        }
        system("pause");
        return 0;
    }

    上述程序只有一个进程,mian函数就是这个进程的主线程,打印结果,是顺序执行,顺序打印。

     现在给它添加一个子线程:

    //线程函数
    unsigned __stdcall  ThreadProc( void *  lpParameter)
    {
        int* p = (int*)lpParameter;
        for (int i = 0; i < *p; i++)
        {
            cout<<"abcdefghij------"<< i<<endl;
        }
        return 0;
    }
    
    int main()
    {
        unsigned nThreadID = 0 ;
        int n = 5;
        HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, &ThreadProc, (LPVOID)&n, 0, &nThreadID );
        CloseHandle(hThread);
        for (int i = 0; i < 5; i++)
        {
            cout<<"1234567890------"<< i<<endl;
        }
        system("pause");
        return 0;
    }

    打印结果如下。可以看到,虽然代码中,主线程的打印部分是写在子线程的后面的,但结果是主线程的打印内容先出来了。

     我们再在主线程和子线程中分别加上一个Sleep,如下:

    //线程函数
    unsigned __stdcall  ThreadProc( void *  lpParameter)
    {
        int* p = (int*)lpParameter;
        for (int i = 0; i < *p; i++)
        {
            Sleep(10);
            cout<<"abcdefghij------"<< i<<endl;
        }
        return 0;
    }
    
    int main()
    {
        unsigned nThreadID = 0 ;
        int n = 5;
        HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, &ThreadProc, (LPVOID)&n, 0, &nThreadID );
        CloseHandle(hThread);
        for (int i = 0; i < 5; i++)
        {
            Sleep(10);
            cout<<"1234567890------"<< i<<endl;
    
        }
        system("pause");
        return 0;
    }

    打印结果如下。可以发现顺序又变了,而且相互影响,没有规律。

    四、线程安全

    上面的最后一个例子,可以发现最后的打印结果没有规律,不可预见,就称之为线程不安全,在实际项目中这样简单地使用多线程代码肯定是不行的。

    线程不安全的原因通常是共享了资源,解决方法是使用加锁来规避。

    锁的核心功能是多个线程访问同一个资源,保证同一时刻只有一个线程能使用该资源,对该资源有独占访问权。

    C++ 11 封装了类mutex,帮我们完成,直接用就行了。

    五、使用锁mutex

    mutex mutex1 ;
    unsigned __stdcall  ThreadProc( void *  lpParameter)
    {
        int* p = (int*)lpParameter;
        for (int i = 0; i < *p; i++)
        {
            mutex1.lock();
            Sleep(10);
            cout<<"abcdefghij------"<< i<<endl;
            mutex1.unlock();
        }
        return 0;
    }
    
    int main()
    {
        
        unsigned nThreadID = 0 ;
        int n = 5;
        HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, &ThreadProc, (LPVOID)&n, 0, &nThreadID );
        CloseHandle(hThread);
        for (int i = 0; i < 5; i++)
        {
            mutex1.lock();
            Sleep(10);
            cout<<"1234567890------"<< i<<endl;
            mutex1.unlock();
        }
        system("pause");
        return 0;
    }

    结果如下:

    其实 最后一行  system("pause"); 也使用了cout,完整的写法应该在它的前后也加上锁。

    否则,如果将Sleep的参数改大,比如我的电脑上,将其改为Sleep(5000),可能出现如下结果:

    六、使用C++11开发多线程

    C++11不仅仅有线程锁的封装,也提供了线程的封装 std::thread

    在C++11之前,我们只能使用windows的 api 来实现多线程,现在抛弃前面创建多线程的那一套吧,直接使用封装好的 std::thread 类。

  • 相关阅读:
    2020去奔跑
    2020前两个月学习心得
    2020年1_2月寒假学习心得
    寒假中期考核个人总结
    Numpy、Matplotlib和pandas学习路线引导
    Python学习路线引导-Python入门基础
    2019在路上
    2020/11/28助教一周小结(第九周)
    2020/11/3助教一周小结(第五周)
    2020/10/25助教一周小结(第四周)
  • 原文地址:https://www.cnblogs.com/hosseini/p/15162745.html
Copyright © 2011-2022 走看看