什么是进程什么是线程?
我的理解是进程是指在系统中正在运行的一个应用程序;程序一旦运行就是进程,或者更专业化来说:进程是指程序执行时的一个实例。
线程是进程的一个实体。
进程——资源分配的最小单位,线程——程序执行的最小单位。
线程进程的区别体现在几个方面:
第一:因为进程拥有独立的堆栈空间和数据段,所以每当启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这对于多进程来说十分“奢侈”,系统开销比较大,而线程不一样,线程拥有独立的堆栈空间,但是共享数据段,它们彼此之间使用相同的地址空间,共享大部分数据,比进程更节俭,开销比较小,切换速度也比进程快,效率高,但是正由于进程之间独立的特点,使得进程安全性比较高,也因为进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。一个线程死掉就等于整个进程死掉。
第二:体现在通信机制上面,正因为进程之间互不干扰,相互独立,进程的通信机制相对很复杂,譬如管道,信号,消息队列,共享内存,套接字等通信机制,而线程由于共享数据段所以通信机制很方便。。
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; }
3、匿名管道(不能网络),用于父子进程间的通信。
4、命名管道(网络)
命名管道的命名规范与邮槽有些类似,对其标识也是采用的UNC格式:
\\Server\Pipe\[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; }
客户端发送消息
// 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; }
5、邮件槽(网络)
6、剪切板
7、动态库,win32动态库的全局数据可以被调用该dll的所有进程共享
//动态库中 #pragma data_seg("MyData") int g_Value=0; //必须赋值,不然不能共享 #pragma data_seg()
8、远程过程调用(RPC,网络)
9、socket(网络)
10、WM_COPYDATA的消息,实用sendmessage向目的窗口发送消息。