// tt.cpp : 定义控制台应用程序的入口点。

//同一进程中的多个线程将共享该进程中的全部系统资源,如虚拟地址空间、文件描述符和信号处理等,但是同一个进程中的多个线程都有各自的调用栈、寄存器环境和线程本地存储。

//线程都拥有自己的堆栈,临界区等主要是控制访问全局变量和成员变量

#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <conio.h>

#define WM_NIHAO  1001
#define THREADNUM 3

/*
volatile 修饰符的作用是告诉编译器无需对该变量作任何的优化,即无需将它放到一个寄存器中,并且该值可被外部改变。
对于多线程引用的全局变量来说,volatile 是一个非常重要的修饰符。
*/
volatile int            gInt=0;
//普通临界区
static CRITICAL_SECTION cs;
//MFC临界区
//CCriticalSection        ccs;

//使用原子操作的方法
volatile LONG aomic=100;

void fun(int tNum)
{
    //printf("第%d线程已经工作了\n",tNum);

if (1)
    {
        //临界区
        EnterCriticalSection(&cs);// 进入临界区,其它线程则无法进入
        //ccs.Lock();
        // 安全访问该区域  
        gInt--;
        printf("第%d线程检测到全局变量gInt的值是:%d\n",tNum,gInt);
        Sleep(0);//致使不使用临界区肯定出错的方法
        gInt++;
        printf("第%d线程检测到全局变量gInt的值是:%d\n",tNum,gInt);
        //ccs.Unlock();
        LeaveCriticalSection(&cs);  // 离开临界区,其它线程可以进入

//原子操作
        InterlockedIncrement(&aomic);
        InterlockedDecrement(&aomic);
    }

MSG   msg;   
    //GetMessage 是 从调用线程的消息队列里取得一个消息并将其放于指定的结构。
    //GetMessage不接收属于其他线程或应用程序的消息。获取消息成功后,线程将从消息队列中删除该消息。函数会一直等待直到有消息到来才有返回值。
    //返回值:如果函数取得WM_QUIT之外的其他消息,返回非零值。如果函数取得WM_QUIT消息,返回值是零。如果出现了错误,返回值是-1。
    while(1)
    {   
        int tGM=::GetMessage(&msg,NULL,0,0);
        switch(msg.message)   
        {   
        case WM_NIHAO:   
            printf("第%d线程接收到主线程的发过来的消息:%s\n",tNum,(char*)msg.wParam);
                break;
        default:
            break;
        }

//不需要接收第二次消息
        if (tGM>0)
        {
            break;
        }
    }

printf("%d线程结束",tNum);

//该函数用于线程终结自身的执行,主要在线程的执行函数中被调用。其中参数dwExitCode用来设置线程的退出码。
    //ExitThread(1);
}
int _tmain(int argc, _TCHAR* argv[])
{
    // 在进入多线程环境之前,初始化临界区  
    InitializeCriticalSection(&cs);

DWORD threadId[THREADNUM];
    HANDLE hThread[THREADNUM];
    for (int i=0;i<THREADNUM;i++)
    {
        hThread[i]=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)fun,(LPVOID)i,CREATE_SUSPENDED,&(threadId[i]));
    }

while (getch()!='1')
    {
        Sleep(100);
    }
    //试探线程读取全局变量
    gInt=100;
    for (int i=0;i<THREADNUM;i++)
    {
        if (-1==ResumeThread(hThread[i]))
        {
            printf("ResumeThread时候出错了\n");
            return -1;
        }
        
    }
    //ResumeThread线程之后线程投入工作需要一定时间
    Sleep(1000);

char buff[20]="ni hao ma?";
    for (int i=0;i<THREADNUM;i++)
    {
        BOOL bPostMes=PostThreadMessage(threadId[i],WM_NIHAO,(WPARAM)buff,(LPARAM)strlen(buff));
    }
    //参数一:线程句柄
    //参数二:等待时间(毫秒),IGNORE:不等,INFINITE:死等
    //返回值: WAIT_OBJECT_0: 线程结束
    //           WAIT_TIMEOUT:超过等待时间,指定的对象处于无信号状态
    //           WAIT_ABANDONED:WAIT_ABANDONED_0至(WAIT_ABANDONED_0 + nCount - 1)如果bWaitAll为TRUE,则返回值表明所有指定对象的状态是触发的,并且至少对象之一,是一个废弃的互斥对象。

//         WAIT_FAILED:出现错误,一般是线程句柄错误

//WaitForSingleObject函数,此函数的作用是监视hHandle的状态,当监视的句柄为有信号状态时,即此对象为空闲状态时,此函数返回,才能执行其后的代码。

//WaitForSingleObject(hThread,INFINITE);

Sleep(1000);
    //值得注意的是hThread数组的所有成员必须全部有效,有一个没效的话,次函数就会执行失败
    DWORD wm=WaitForMultipleObjects(THREADNUM,hThread,true,10000);
    //若果第三个参数是true,则返回值代表所有线程的状态
    //若果第三个参数是false,则返回值代表那个有返回线程的状态
    switch (wm)
    {
    case WAIT_FAILED:
        printf("wm-->WAIT_FAILED\n");
        break;
    case WAIT_TIMEOUT:
        printf("wm-->WAIT_TIMEOUT\n");
        break;

case WAIT_ABANDONED_0+0:
    case WAIT_ABANDONED_0+1:
    case WAIT_ABANDONED_0+2:
        printf("wm-->WAIT_ABANDONED_0\n");
        break;

case WAIT_OBJECT_0+0:
    case WAIT_OBJECT_0+1:
    case WAIT_OBJECT_0+2:
        printf("wm-->WAIT_OBJECT_0\n");
        break;

default:
        break;
    }

//一般情况下,线程运行结束之后,线程函数正常返回,但是应用程序可以调用TerminateThread强行终止某一线程的执行。
    for (int i=0;i<THREADNUM;i++)
    {
        TerminateThread(hThread[i],0);
    }

// 释放临界区资源,当不再使用临界区时调用该函数  
    DeleteCriticalSection(&cs);  
    return 0;
}

多线程学习(一)----CreateThread相关推荐

  1. Delphi多线程详解CreateThread、TThread,以及线程间通过临界区(CriticalSection)实现同步

     在了解多线程之前我们先了解一下进程和线程的关系 一个程序至少有一个主进程,一个进程至少有一个线程. 为了保证线程的安全性请大家看看下面介绍 Delphi多线程同步的一些处理方案大家可以参考:ht ...

  2. Java多线程学习处理高并发问题

    在程序的应用程序中,用户或请求的数量达到一定数量,并且无法避免并发请求.由于对接口的每次调用都必须在返回时终止,因此,如果接口的业务相对复杂,则可能会有多个用户.调用接口时,该用户将冻结. 以下内容将 ...

  3. C#多线程学习(四) 多线程的自动管理(线程池) (转载系列)——继续搜索引擎研究...

    在多线程的程序中,经常会出现两种情况: 一种情况:   应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应                   这一般使用ThreadPo ...

  4. 艾伟:C#多线程学习(六) 互斥对象

    本系列文章导航 C#多线程学习(一) 多线程的相关概念 C#多线程学习(二) 如何操纵一个线程 C#多线程学习(三) 生产者和消费者 C#多线程学习(四) 多线程的自动管理(线程池) C#多线程学习( ...

  5. C# 多线程学习总结

    C#多线程学习(一) 多线程的相关概念 什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源.而一个进程又是由多个线程所组成的. 什么是线程? 线程是 ...

  6. C#多线程学习(二) 如何操纵一个线程

    C#多线程学习(二) 如何操纵一个线程 原文链接:http://kb.cnblogs.com/page/42529/ [1] C#多线程学习(二) 如何操纵一个线程 [2] C#多线程学习(二) 如何 ...

  7. 多线程学习-基础(四)常用函数说明:sleep-join-yield

    一.常用函数的使用 (1)Thread.sleep(long millis):在指定的毫秒内让当前正在执行的线程休眠(暂停执行),休眠时不会释放当前所持有的对象的锁. (2)join():主线程等待子 ...

  8. C#多线程学习之(五)使用定时器进行多线程的自动管理

    本文实例讲述了C#多线程学习之使用定时器进行多线程的自动管理.分享给大家供大家参考.具体分析如下: Timer类:设置一个定时器,定时执行用户指定的函数. 定时器启动后,系统将自动建立一个新的线程,执 ...

  9. java多线程学习-java.util.concurrent详解

    http://janeky.iteye.com/category/124727 java多线程学习-java.util.concurrent详解(一) Latch/Barrier 博客分类: java ...

  10. C#多线程学习(三) 生产者和消费者

    C#多线程学习(三) 生产者和消费者 原文链接:http://kb.cnblogs.com/page/42530/ 本系列文章导航 C#多线程学习(一) 多线程的相关概念 C#多线程学习(二) 如何操 ...

最新文章

  1. CSS中浮动布局float(小米布局案例、导航栏案例、overflow)
  2. 高斯-赛德尔迭代法简介
  3. 主成分分析的数学原理
  4. sql server charindex函数和patindex函数详解(转)
  5. 如何学习开源项目及Ceph的浅析
  6. java怎样写入五个人的成绩_用java输入5个学员姓名和分数,显示分数最高的学员姓名和分数?...
  7. STL(二)——向量vector
  8. 爬取豌豆荚app数据(总结篇)
  9. Pytorch模型量化介绍
  10. STM32开发 | 移远4G-Cat.1模组EC200N-CN开发
  11. 校园一角 四年级计算机课,校园一角的四年级作文5篇
  12. 重磅发布 | 图像图形学发展年度报告【中国图象图形学报第6期综述专刊】
  13. 概率论中一个有趣的问题-------双六问题
  14. 游戏建模:21个人脑壳雕刻小小技巧,非常有用!
  15. 华光昱能光知识-细说MPO光纤跳线那些事
  16. 新旧电脑安装win11系统【超简单教程】
  17. 用NDK-r25编译libpng
  18. 【linux】重新启动项目
  19. wordpress建站准备教程(一)域名:域名备案、域名注册、域名绑定、域名解析
  20. web项目:智能出行规划网站——爬虫+flask+echarts+基础前端(html、css、js、jq)

热门文章

  1. 数据库-优化-MYSQL数据库设计原则
  2. 解决2次查询User的问题(ThreadLocal)
  3. Zuul:Cookie和动态路由
  4. SpringBoot服务整合(整合邮件服务、定时调度、Actuator监控)
  5. java的自动装配是什么意思_java – 什么时候在Spring中使用自动装配
  6. 判断ipad还是安卓_?谷歌认输,iPad或成唯一赢家,安卓平板路在何方?
  7. PHP 梯形图,学习PLC,不要先翻资料,干就完了.搞起你的第一个梯形图.
  8. 一步一步了解Promise原理
  9. vCenter 6.0安装部署
  10. Linux打tar包排除目录中的某个目录