VC++学习(15):多线程

1.       程序,进程,线程

A: 程序是计算机指令的集合,它以文件的形式存储在磁盘上,而进程通常被定义为一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动.一个程序可以对应多个进程.

进程是资源申请,高度和独立运行的单位,因此,它使用系统中的运行资源,而程序不能申请系统资源,不能被系统高度也不能作为独立运行的单位,因此它不占系统运行资源.

进程组成:

<1>   操作系统用来管理进行的内核对象

         内核对象也是系统用来存放关于进程的统计信息的地方.内核对象是操作系统内部分配的一个内在块,该内存块是一种数据结构,其成员负责维护该对象的各种信息.

<2>   地址空间

         它包含所有可执行模块或DLL模块的代码和数据.另外,它也包含动态内存分配的空间,例如线程的栈和堆分配空间

B: 进程从来不执行任何东西,它只是纯种的容器,若要使进行完成某项操作,它必须拥有一个在它的环境中运行的纯种,此线程负责执行包含在进程的地址空间的中的代码.也就是,真正完成代码执行的是线程,而进行只是纯种的容器,或者说是线程的执行环境.

单个进程可能包含若干个纯种,这些线程都同时执行进行地址空间的中代码,每个线程至少拥有一个纯种,来执行进行的地址空间中的代码,当创建一个进程时,操作系统会自动创建这个进程的第一个线程,称为主纯种.也就是执行main函数或WinMain函数的线程,可以把main函数或WinMain函数看作是主线程的入口点函数.此后,主线程可以创建其它的线程.

C: 线程也由两部分组成:

<1> 线程内核对象,操作系统用它来对线程实施管理,内核对象也是系统用来存放线程统计信息的地地方

<2>线程栈stack:它用于维护线程在执行代码时需要的所有函数参数和局部变量.线程总是在某个进程环境中创建,系统从进程的地址空间中分配内存,供线程的栈使用.

线程运行:

操作系统为每一个运行线程安排一定的CPU时间时间片,系统通过一种循环的方式为线程提供时间片,线程在自己的时间内运行,因时间片相当短,因此给用户的感觉就是好像多个线程是同时运行一样.

2.       创建线程的SDK函数

HANDLE CreateThread (

 SEC_ATTRS SecurityAttributes,//安全结构全指针,NULL为默认安全性

 ULONG StackSize,//线程初始栈的大小 ,以字节为单位,如果为0或小于0,默认将使用

//与调用该线程相同的栈空间大小

 SEC_THREAD_START StartFunction,//线程处理函数地址(可用函数名)

 PVOID ThreadParameter,//可以为数据或其它信息的指针,表示给新线程传送参数

 ULONG CreationFlags,//线程创建的标记,可以为0CREATE_SUSPENDED,如果

//CREATE_SUSPENDED线程创建后暂停状态,直到程序调用//ResumeThread,如果为0立即运行

 PULONG ThreadId //[out]返回值,系统分配新的线程ID

);

3.    一个简单的多线程程序,模拟售票系统

#include <windows.h>//必要的头文件,使用Windows API函数

#include <iostream.h>

int index = 0;

int tickets = 100;//票数

HANDLE hMutex; //使用全局的互斥对象来保证对同一资源的互斥访问与操作这里是tickets

//线程处理函数原型,形式可从MSDN中拷贝

//线程1 的入口函数

DWORD WINAPI Fun1Proc(

         LPVOID lpParameter   // thread data

);

DWORD WINAPI Fun2Proc(

         LPVOID lpParameter   // thread data

);

void main(){

         HANDLE hThread1;

         DWORD thread1ID;

     //创建线程1

         hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, &thread1ID);

         HANDLE hThread2;

         DWORD thread2ID;

         //创建线程2

         hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, &thread2ID);

         CloseHandle(hThread1); //关闭线程的句柄,为什么要关闭?它将线程的使用计数减1

CloseHandle(hThread2);//这样当线程结束时,线程内核对象被释放,

//否则只有当进程结束,才释放线程的内核对象hThread1hThread2

    //创建一个互斥对象,如果成功返回互斥对象的句柄,否则返回NULL

         hMutex = CreateMutex(NULL, FALSE, "tickets");

         if (hMutex)

         {

                   if(ERROR_ALREADY_EXISTS == GetLastError())

                   {

                            cout << "only one instance can run!" << endl;

                            return;

                   }

         }

//      while(index++ < 100)

//      {

//                cout << "main Thread is running!" << endl;

//      }

         Sleep(4000);//主线程睡眠4秒钟,给其它线程运行的时间,因为一旦主线程退出则进行退出,其它线程也将退出

}

DWORD WINAPI Fun1Proc(

                                                        LPVOID lpParameter   // thread data

                                                        ){

//      while(index++ < 100)

//                cout << "Thread1 is running!" + index << endl;

        

         while(TRUE){

                   WaitForSingleObject(hMutex, INFINITE);//如果全局互斥对象是有信号状态,则获得该对象,

//直到调用ReleaseMutex之前,互斥对象是无信号状态,其它线程不能对互斥对象进行访问

                   if(tickets > 0)

                   {

                            Sleep(1);

                            cout << "Thread1 sell tickets : " << tickets-- << endl;

                   }

                   else

                            break;

                   ReleaseMutex(hMutex);//将互斥对象设置为有信号状态

         }

         return 0;

}

DWORD WINAPI Fun2Proc(

         LPVOID lpParameter   // thread data

)

{      

         while(TRUE){

                   WaitForSingleObject(hMutex, INFINITE);

                   if (tickets > 0)

                   {

                            Sleep(1);

                            cout << "Thread2 sell tickets : " << tickets-- << endl;

                   }

                   else

                            break;

                   ReleaseMutex(hMutex);

         }

         return 0;

}

 

3.       多线程聊天程序

(1)      加载套接字库在InitInstance()中,调用AfxSocketInit(),此时可以不加载库文件,但要加入Afxsock.h"头文件

if (!AfxSocketInit())

       {

              AfxMessageBox("加载套接字库失败!");

              return FALSE;

       }

(2)      CChatDlg.h中类的声明外,创建一个全局的结构体,包含套接字和窗口的句柄值,主要是在投送消息时可以将两个需要传送的消息同时发送

struct RECVPARAM{

       SOCKET socket;

       HWND hWnd;

};

(3)      CChatDlg中创建成员变量m_socket,然后增加一个成员函数,IniSocket(),在其中完成m_socket的初始化和绑定。

BOOL CChatDlg::InitSocket()

{

         m_socket = socket(AF_INET, SOCK_DGRAM, 0);

         if (INVALID_SOCKET == m_socket)

         {

                   MessageBox("创建套接字失败!");

                   return FALSE;

         }

         SOCKADDR_IN addrSock;

         addrSock.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

         addrSock.sin_family = AF_INET;

         addrSock.sin_port = htons(6010);

        

         int bindRst;

         bindRst = bind(m_socket, (SOCKADDR*)&addrSock, sizeof(SOCKADDR));

         if (SOCKET_ERROR == bindRst)

         {

                   closesocket(m_socket);

                   MessageBox("绑定失败!");

                   return FALSE;

         }

         return TRUE;

}

(4)      .创建一个线程,CreateThread(),须将线程函数RecvProc定义为静态的或者全局函数。因为对于运行的代码来说,它不知道要产生哪一个对象,即运行时根本不知道如何去产生一个CChatDialog类的对象,对于运行时代码来说,如果要调用纯种函数来启动某个纯种的话,应该不需要产生某个对象就可以调用这个纯种函数.因此要定义为类的静态成员或全局函数,即这个代码是所以对象共有的,不需要定义对象才能使用.

static DWORD WINAPI RecvProc(LPVOID lpParameter);

DWORD WINAPI CChatDlg::RecvProc(LPVOID lpParameter)

{

     //根据线程函数参数获取发送端套接字和要设置包含文本控件的窗口句柄

         SOCKET socket = ((RECVPARAM*)lpParameter)->socket;

         HWND hWnd = ((RECVPARAM*)lpParameter)->hWnd;//窗口句柄

         SOCKADDR_IN addrRecv;

         int len = sizeof(SOCKADDR);      

         char recvBuf[100];

         char tempBuf[100];

         int recvRst;

         CString recvStr;

         while(TRUE)

         {

         //接收数据

                   recvRst = recvfrom(socket, recvBuf, 100, 0, (SOCKADDR*)&addrRecv, &len);

                   if (SOCKET_ERROR == recvRst)

                   {

                            break;

                   }

                   sprintf(tempBuf, "%s:%s\r\n", inet_ntoa(addrRecv.sin_addr), recvBuf);

//                recvStr += tempBuf;//这种方式更加简单不用使用消息

//                ::SetDlgItemText(hWnd, IDC_EDIT_RECV, recvStr);

                   //使用自定义消息的方式来对控件填充内容

                   ::PostMessage(hWnd, WM_RECVDATA, 0, (LPARAM)tempBuf);

         }      

         return 0;

}

OnInitDialog中调用InitSocket完成初始化工作。

InitSocket();//初始化套接字库

         RECVPARAM *pRecvParm = new RECVPARAM();

         pRecvParm->socket = m_socket;

         pRecvParm->hWnd = m_hWnd;

        

         HANDLE hThreadRecv = CreateThread(NULL, 0, RecvProc, (LPVOID)pRecvParm, 0, NULL);

         CloseHandle(hThreadRecv);

 

(5)      ::PostMessage()完成将收到的数据发送给对话框。用自定义的消息参考下面的代码。注意要将EDitBoxMultiLine属性选上。

<1>:       ChatDlg.h#define WM_RECVDATA WM_USER+1

<2>:       afx_msg void OnRecvData(WPARAM wParam,LPARAM lParam);

<3>:       ChatDlg.cppON_MESSAGE(WM_RECVDATA,OnRecvData)

      

void CChatDlg::OnRecvData(WPARAM wParam,LPARAM lParam)

{

CString str=(char*)lParam;

CString strTemp;

GetDlgItemText(IDC_EDIT_RECV,strTemp);

str+="\r\n";

str+=strTemp;

SetDlgItemText(IDC_EDIT_RECV,str);

}

(6)      最后在DWORD WINAPI CChatDlg::RecvProc(LPVOID lpParameter)

中调用 ::PostMessage(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);

//不能用SendMessage()

4.对发送按纽的响应代码:

void CChatDlg::OnBtnSend()

{

// TOD Add your control notification handler code here

DWORD dwIP;

((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);

 

SOCKADDR_IN addrTo;

addrTo.sin_family=AF_INET;

addrTo.sin_port=htons(6000);

addrTo.sin_addr.S_un.S_addr=htonl(dwIP);

 

CString strSend;

GetDlgItemText(IDC_EDIT_SEND,strSend);

sendto(m_socket,strSend,strSend.GetLength()+1,0,

 (SOCKADDR*)&addrTo,sizeof(SOCKADDR));

SetDlgItemText(IDC_EDIT_SEND,"");

}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/267696.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在linux中500g怎么分区,500G的硬盘,怎么分区比较合理?

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼个人电脑怎么分&#xff1f;希望大家给我规范一下另外&#xff0c;我的/dev/sda2用的是xfs&#xff0c;/dev/sda9用的是reiser4draplaterDrapl ~ $ sudo fdisk -l密码&#xff1a;Disk /dev/sda: 160.0 GB, 160041885696 bytes255 …

CSS宽高背景介绍

本萌新还未毕业&#xff0c;在一家外包公司干了一个月&#xff0c;因烦恼日常琐事任务&#xff0c;深感外包之坑&#xff0c;以及上班路途艰辛&#xff0c;特转战erp实施&#xff0c;继写日常随笔&#xff0c;望来日屌丝逆袭&#xff0c;走上人生巅峰。 若有错误&#xff0c;请…

linux交叉编译生成的是什么,静态链接util-linux – 交叉编译(生成动态链接文件)...

我一直试图交叉编译util-linux for arm但我一直以动态链接的可执行文件结束,我不知道为什么会这样.我的目标是静态的.我在使用类似步骤的不同工具之前进行了交叉编译,并且它一直有效,所以这次我不知道我做错了什么.我正在使用Ubuntu 16.04.以下是我正在运行的命令&#xff1a;e…

维护SAP帮助信息(WEB)

维护SAP帮助信息&#xff08;WEB&#xff09; 1、首先进入000 Client &#xff0c;然后scc4修改000的模式 2、spro 3、 4、 5、注意 服务器为&#xff1a;http://help.sap.com 路径为&#xff1a;saphelp_erp60_sp/helpdata 转载于:https://blog.51cto.com/anhwei/626111

读jQuery之十二(删除事件核心方法)

使用jQuery删除事件&#xff08;或称解除事件绑定&#xff09;有三个函数&#xff1a;unbind、die和undelegate。这三个方法都依赖于未公开的jQuery.event.remove&#xff08;后续使用remove简写&#xff09;。此为删除事件的核心方法。remove 所作的事情与上一篇提到的.add 刚…

像Excel一样使用python进行数据分析(1)

&#xff08;虽然是转载&#xff0c;但是是我每块都测试过得&#xff0c;容易出问题的地方我会添加一些自己的经验&#xff0c;仅供参考&#xff09; 像Excel一样使用python进行数据分析&#xff08;2&#xff09; 像Excel一样使用python进行数据分析&#xff08;3&#xff09;…

linux svn 自动部署,linux下svn安装和自动部署

liunx系统下安装svn并自动更新到项目中三部分&#xff1a;【1.安装 2.建立svn库 3.配置文件】html查看是否安装了svn 【svn –version】若是安装了svn能够先进行卸载 【sudo apt-get remove –purge subversion】接下来进行安装 【sudo apt-get install subversion】当安装成功…

css优先级

很经典的文章啊 [转]CSS的优先级特性Specificity 如果同个元素有两个或以上冲突的CSS规则&#xff0c;浏览器有一些基本的规则来决定哪一个非常特殊而胜出。它可能不像其它那么重要&#xff0c;大部分案例你不需要担心冲突&#xff0c;但大型而且复杂的CSS文件&#xff0c;或有…

linux编译准备,Linux 下编译指南

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼首先&#xff0c;我们需要获取最新的源码。使用如下命令即可&#xff1a;git clone -b master --depth1 https://github.com/CleverRaven/Cataclysm-DDA.git这条命令会只获取 master 分支下最新的代码。对于普通玩家来说这已经足够…

ServletConfig的详解

一、Servlet的作用 servlet是在servlet容器内运行的程序&#xff0c;有时需要访问容器外部或借助容器访问外部资源。所以可以通过web.xml文件中为某个servlet配置的名称和参数等信息传递给servlet。 这样做的好处是&#xff1a;如果将重要的信息、编码方式等配置信息放在web.xm…

You can't specify target table 'tablename' for update in FROM clause的解决方法

在执行下面的sql时报错 DELETE FROM temp WHERE id IN (SELECT a.id FROM t_user t JOIN temp a ON t.email a.email); 这种写法在sqlserver或者oracle中是支持的&#xff0c;但是mysql目前是不支持的&#xff0c;在mysql中不能同时查询一个表的数据再同时进行删除&#xff0…

linux缓存代码,Linux使用的缓存

Linux使用的缓存6.7.1 Linux使用的缓存不管在硬件设计还是软件设计中&#xff0c;高速缓存是获得高性能的常用手段。Linux 使用了多种和内存管理相关的高速缓存。1&#xff0e; 缓冲区高速缓存&#xff1a;缓冲区高速缓存中包含了由块设备使用的数据缓冲区。这些缓冲区中包含了…

Linux查看文件夹大小du

du命令参数详解见&#xff1a; http://baike.baidu.com/view/43913.htm 下面我们只对其做简单介绍&#xff1b; 查看linux文件目录的大小和文件夹包含的文件数 统计总数大小 du -sh filename&#xff08;其实我们经常用du -sh *&#xff0c;显示当前目录下所有的文件及其大小&a…

cursor: mutex S等待事件

cursor: mutex * events等待事件 cursor: mutex * events等待事件用于Cursor Parent 和 Cursor stats类型的操作&#xff1a; ‘Cursor: Mutex S’ &#xff0c; 某个进程以SHRD S mode申请一个Mutex&#xff0c; 而该Mutex要么被其他进程已EXCL X mode所持有&#xff0c;要么其…

flash和linux文件系统,面向大容量Flash的高效Linux文件系统改进和实现

摘要&#xff1a;文件系统是Linux操作系统的重要组成部分.而本文对于面向大容量Flash文件系统高效性的研究则主要侧重于对于嵌入式Linux操作系统文件系统的研究.由于嵌入式设备介质的特殊性,电源系统的不稳定性以及Flash容量的日益增长,大容量Flash设备文件系统高效性方面的问题…

Xshell配置ssh免密码登录-密钥公钥(Public key)

1 简介 ssh登录提供两种认证方式&#xff1a;口令(密码)认证方式和密钥认证方式。其中口令(密码)认证方式是我们最常用的一种&#xff0c;这里介绍密钥认证方式登录到linux/unix的方法。 使用密钥登录分为3步&#xff1a; 1、生成密钥&#xff08;公钥与私钥&#xff09;&#…

Linux面试题集锦,测测你的水平(答案)四

三&#xff0e;简答题&#xff1a;1&#xff0e;简述Linux文件系统通过i节点把文件的逻辑结构和物理结构转换的工作过程。参考答案&#xff1a;Linux通过i节点表将文件的逻辑结构和物理结构进行转换。i节点是一个64字节长的表&#xff0c;表中包含了文件的相关信息&#xff0c;…

linux vim ctags,Linux环境上代码阅读与编写的利器-vim+ctags+cscope

Linux环境下代码阅读与编写的利器----vimctagscscope所谓工欲善其事&#xff0c;必先利其器。从事Linux程序开发&#xff0c;特别是Linux驱动程序的开发&#xff0c;不管是通过windows下虚拟一个Linux,还是通过samba访问Linux都是很郁闷的事情。原因就是程序本身需要Linux的编译…

oracle vm中的xp添加共享文件夹

接着就可以在虚拟的电脑系统里面打开我们的共享文件夹&#xff0c;在桌面找到”网络邻居“&#xff0c;双击打开 我们需要通过”添加一个网络邻居“来加载我们刚才添加的”共享文件夹“&#xff0c;根据向导一步步执行 然后点击”浏览“ 展开前面的 ”“ visualBox shared fold…

MSBuild + MSILInect实现编译时AOP-改变前后对比

实现静态AOP&#xff0c;就需要我们在预编译时期&#xff0c;修改IL实现对代码逻辑的修改。Mono.Cecil就是一个很好的IL解析和注入框架&#xff0c;参见编译时MSIL注入--实践Mono Cecil(1)。 我的思路为&#xff1a;在编译时将加有继承制MethodInterceptBaseAttribute标签的原方…