• 线程和进程


    什么是进程什么是线程?

    我的理解是进程是指在系统中正在运行的一个应用程序;程序一旦运行就是进程,或者更专业化来说:进程是指程序执行时的一个实例。
    线程是进程的一个实体。
    进程——资源分配的最小单位,线程——程序执行的最小单位。

    线程进程的区别体现在几个方面:

    第一:因为进程拥有独立的堆栈空间和数据段,所以每当启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这对于多进程来说十分“奢侈”,系统开销比较大,而线程不一样,线程拥有独立的堆栈空间,但是共享数据段,它们彼此之间使用相同的地址空间,共享大部分数据,比进程更节俭,开销比较小,切换速度也比进程快,效率高,但是正由于进程之间独立的特点,使得进程安全性比较高,也因为进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。一个线程死掉就等于整个进程死掉。

    第二:体现在通信机制上面,正因为进程之间互不干扰,相互独立,进程的通信机制相对很复杂,譬如管道,信号,消息队列,共享内存,套接字等通信机制,而线程由于共享数据段所以通信机制很方便。。

    3.属于同一个进程的所有线程共享该进程的所有资源,包括文件描述符。而不同过的进程相互独立。

    4.线程又称为轻量级进程,进程有进程控制块,线程有线程控制块;

    5.线程必定也只能属于一个进程,而进程可以拥有多个线程而且至少拥有一个线程;

    第四:体现在程序结构上,举一个简明易懂的列子:当我们使用进程的时候,我们不自主的使用if else嵌套来判断pid,使得程序结构繁琐,但是当我们使用线程的时候,基本上可以甩掉它,当然程序内部执行功能单元需要使用的时候还是要使用,所以线程对程序结构的改善有很大帮助。

    进程与线程的选择取决以下几点:

    1、需要频繁创建销毁的优先使用线程;因为对进程来说创建和销毁一个进程代价是很大的。

    2、线程的切换速度快,所以在需要大量计算,切换频繁时用线程,还有耗时的操作使用线程可提高应用程序的响应

    3、因为对CPU系统的效率使用上线程更占优,所以可能要发展到多机分布的用进程,多核分布用线程;

    4、并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求;

    5、需要更稳定安全时,适合选择进程;需要速度时,选择线程更好。

    以上这一段来自https://www.cnblogs.com/renzhuang/articles/6733461.html

    线程创建函数有beginthread,windows上有CreateThread函数。

    DWORD WINAPI FucThread(LPVOID lpParam)
    {
        while(true)
        {
            
        }
        return 1;
    }
    
    DWORD dwThread = 0;
    HANDLE hThread = CreateThread(NULL,0,FucThread,0,0,&dwThread);

    线程同步有4种方法:临界区(单进程内线程同步)、互斥量、事件对象(可对进程外的线程同步)、信号量(跨进程、可以指定允许个数)。

    临界区用法:

    CRITICAL_SECTION g_cri;
    InitializeCriticalSection(&g_cri);  //初始化临界区
    EnterCriticalSection(&g_cri); 
    //.....操作......

    LeaveCriticalSection(&g_cri);

    互斥量用法:

    HANDLE g_mutex;  //互斥量

    g_mutex = CreateMutex(NULL, false, NULL); //创建互斥量
     WaitForSingleObject(g_mutex, INFINITE); 
    //..............
    ReleaseMutex(g_mutex);

    事件用法:

    HANDLE g_event = CreateEvent(NULL, false, false, NULL);
    SetEvent(g_event);//刚开始要设成有信号状态
    
    WaitForSingleObject(g_event, INFINITE);
    //.......
    SetEvent(g_event);

    信号量用法:

    HANDLE g_semaphore;
    g_semaphore = CreateSemaphore(NULL, 1, 1, NULL);   //信号量
    //第二个参数当前信号量,第三个参数最大信号量,当前信号量等于0时会造成堵塞。
    
    WaitForSingleObject(g_semaphore, INFINITE);
    //.........
    ReleaseSemaphore(g_semaphore, 1, NULL);

     程序中比较实用的还有读写锁。在Windows平台,Vista和Server 2008及其更高的版本才开始提供读写锁相关的API,如果需要支持XP系统,那么往往需要自己实现读写锁机制。

    注释:CreateEvent的第2个参数为true表示需要手动复原,第3个参数表示一开始就是有信号;其中m_sevent主要是为了不让写锁等读锁。如果连续几个读锁,等所有读锁好了之后在轮到写锁,明显不合理。

    // 实现代码
    
    class RWLock
    {
    public:
        RWLock();
        ~RWLock();
    public:
        void AcquireReadLock();
        void ReleaseReadLock();
        void AcquireWriteLock();
        void ReleaseWriteLock();
    private:
        volatile DWORD      m_cnt;
        CRITICAL_SECTION    m_cs;
        HANDLE              m_evt;
        HANDLE              m_sem;
    };
    
    RWLock::RWLock()
        : m_cnt(0)
        , m_evt(NULL)
        , m_cs(NULL)
        , m_sem(NULL)
    {
        // 提倡的做法在专门的初始化函数里创建和初始化这些变量
    
        ::InitializeCriticalSection(&m_cs);
    
        // Event必须是手动重置,否则存在死锁隐患,即等待Event前,先被激活了
        m_evt = ::CreateEvent(NULL, TRUE, TRUE, NULL);
        m_sem = ::CreateSemaphore(NULL, 1, 1, NULL);
    }
    
    RWLock::~RWLock()
    {
        ::CloseHandle(m_sem);
        ::CloseHandle(m_evt);
        ::DeleteCriticalSection(&m_cs);
    }
    
    void RWLock::AcquireReadLock()
    {
        ::WaitForSingleObject(m_evt, INFINITE);
    
        ::EnterCriticalSection(&m_cs);
        if(0 == m_cnt++)
            ::WaitForSingleObject(m_sem, INFINITE);
        ::LeaveCriticalSection(&m_cs);
    }
    
    void RWLock::ReleaseReadLock()
    {
        ::EnterCriticalSection(&m_cs);
        if(0 == --m_cnt)
            ::ReleaseSemaphore(m_sem, 1, NULL);
        ::LeaveCriticalSection(&m_cs);
    }
    
    void RWLock::AcquireWriteLock()
    {
        ::ResetEvent(m_evt);
        ::WaitForSingleObject(m_sem, INFINITE);
    }
    
    void RWLock::ReleaseWriteLock()
    {
        ::ReleaseSemaphore(m_sem, 1, NULL);
        ::SetEvent(m_evt);
    }
    
    // 使用示例
    
    void Read()
    {
        // 多个线程能够同时进行读操作
    
        rwLock.AcquireReadLock();
    
        // 读取数据
        
        rwLock.ReleaseReadLock();
    }
    
    void Write()
    {
        rwLock.AcquireWriteLock();
    
        // 写入数据
    
        rwLock.ReleaseWriteLock();
    }

    进程间同步的方式有:进程间通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存等)、以及套接字socket。

      1、文件IO操作或者文件映射

      2、共享内存

    #include <windows.h>
    #include <iostream>
    #include <string>
    #include <cstring>
    using namespace std;
    
    int main()
    {
        string strMapName("ShareMemory");                // 内存映射对象名称
        string strComData("This is common data!");        // 共享内存中的数据
        LPVOID pBuffer;                                    // 共享内存指针
    
        // 首先试图打开一个命名的内存映射文件对象  
        HANDLE hMap = ::OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, strMapName.c_str());
        if (NULL == hMap)
        {    // 打开失败,创建之
            hMap = ::CreateFileMapping(INVALID_HANDLE_VALUE,
                                       NULL,
                                       PAGE_READWRITE,
                                       0,
                                       strComData.length()+1,
                                       strMapName.c_str());
            // 映射对象的一个视图,得到指向共享内存的指针,设置里面的数据
            pBuffer = ::MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
            strcpy((char*)pBuffer, strComData.c_str());
            cout << "写入共享内存数据:" << (char *)pBuffer << endl;
        }
        else
        {    // 打开成功,映射对象的一个视图,得到指向共享内存的指针,显示出里面的数据
            pBuffer = ::MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
            cout << "读取共享内存数据:" << (char *)pBuffer << endl;
        }
    
        getchar();            // 注意,进程关闭后,所有句柄自动关闭,所以要在这里暂停
    
        // 解除文件映射,关闭内存映射文件对象句柄
        ::UnmapViewOfFile(pBuffer);
        ::CloseHandle(hMap);
        system("pause");        
        return 0;
    }
    View Code

      3、匿名管道(不能网络),用于父子进程间的通信。

      4、命名管道(网络)

       命名管道的命名规范与邮槽有些类似,对其标识也是采用的UNC格式:

    \ServerPipe[Path]Name  

        其中,第一部分\Server指定了服务器的名字,命名管道服务即在此服务器创建,其字串部分可表示为一个小数点(表示本机)、星号(当前网络字段)、域名或是一个真正的服务;第二部分Pipe与邮槽的Mailslot一样是一个不可变化的硬编码字串,以指出该文件是从属于NPFS;第三部分[Path]Name则使应用程序可以唯一定义及标识一个命名管道的名字,而且可以设置多级目录。

      服务端接受消息

    // vckzt.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include"cstdlib"
    #include "windows.h"
    #include "iostream"
    using namespace std;
    #define PIPE_NAME L"\\.\Pipe\test"
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        char buffer[1024];
        DWORD ReadNum;
    
        HANDLE hPipe = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 0, 0, 1000, NULL);
        if (hPipe == INVALID_HANDLE_VALUE)
        {
            cout << "创建命名管道失败!" << endl;
            CloseHandle(hPipe);
            return 0;
        }
    
        if (ConnectNamedPipe(hPipe, NULL) == FALSE)
        {
            cout << "与客户机连接失败!" << endl;
            CloseHandle(hPipe);
            return 0;
        }
        cout << "与客户机连接成功!" << endl;
    
        while (1)
        {
            if (ReadFile(hPipe, buffer, 1024, &ReadNum, NULL) == FALSE)
            {
                cout << "读取数据失败!" << endl;
                break;
            }
    
            buffer[ReadNum] = 0;
            cout << "读取数据:" << buffer << endl;
        }
    
        cout << "关闭管道!" << endl;
        CloseHandle(hPipe);
    
        system("Pause");
        return 0;
    }
    View Code

      客户端发送消息

    // test2.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include"cstdlib"
    #include "windows.h"
    #include "iostream"
    using namespace std;
    #define PIPE_NAME L"\\.\Pipe\test"
    
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        char buffer[1024];
        DWORD WriteNum;
    
        if (WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER) == FALSE)
        {
            cout << "等待命名管道实例失败!" << endl;
            return 0;
        }
    
        HANDLE hPipe = CreateFile(PIPE_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hPipe == INVALID_HANDLE_VALUE)
        {
            cout << "创建命名管道失败!" << endl;
            CloseHandle(hPipe);
            return 0;
        }
        cout << "与服务器连接成功!" << endl;
        while (1)
        {
            gets(buffer);//等待数据输入
            if (WriteFile(hPipe, buffer, strlen(buffer), &WriteNum, NULL) == FALSE)
            {
                cout << "数据写入管道失败!" << endl;
                break;
            }
        }
    
        cout << "关闭管道!" << endl;
        CloseHandle(hPipe);
    
        return 0;
    }
    View Code

      5、邮件槽(网络)

        优点:通过网络,将一条消息广播给一台或多台计算机。
        缺点:只允许从客户机到服务器,建立一种不可靠单向数据通信。不提供数据可靠性传播的保障。

      6、剪切板

      7、动态库,win32动态库的全局数据可以被调用该dll的所有进程共享

    //动态库中
     #pragma data_seg("MyData")
         int g_Value=0;   //必须赋值,不然不能共享
     #pragma data_seg()    

      8、远程过程调用(RPC,网络)

      9、socket(网络)

      10、WM_COPYDATA的消息,实用sendmessage向目的窗口发送消息。

    转载于:https://www.cnblogs.com/jlyg/p/10360096.html

  • 相关阅读:
    解析url
    初学react
    移动端判断用户滑动方向
    冒泡排序、快速排序、数组去重
    Angular Material主题配置
    reset()方法的使用、jq下面reset()的正确使用方法
    conts、var 、let的区别
    NLP
    mybatis的执行流程
    PHP递归算法示例:打印无限级数组元素的值
  • 原文地址:https://www.cnblogs.com/twodog/p/12135198.html
Copyright © 2020-2023  润新知