多线程01

时间:2021-08-19
本文章向大家介绍多线程01,主要包括多线程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 类。

原文地址:https://www.cnblogs.com/hosseini/p/15162745.html