多线程的些许理解

一.体系架构

1.原子操作 

1) 定义

 不可中断的一个或者一系列操作,也就是不会被线程调度机制打断的操作,在运行期间不会有任何的上下文切换(context switch).

2) 我们为什么关注原子操作(what)

 在多线程操作的时候,确定某个操作非原子操作,要用消耗性能的昂贵的锁去保护。

3)单核CPU、多核CPU

在单核CPU中,能够在一个指令中完成的操作都可以看作为原子操作,因为中断只发生在指令间。

在多核CPU中,软件级别的原子操作依赖于硬件支持的,在X86体系中,CPU提供了HLOCK pin引 线,允许CPU在执行某个指令(仅仅是一个指令)时拉低HLOCK pin引线的电平,直到这个这个指令执行完毕才放开。拉低电平导致锁住总线,如此在同一总线的CPU就暂时无法通过总线访问内存了,这样就保证了多核处理器的原子操作。(在那段时间,我猜测,处理器,在想为啥内存访问被禁止,什么鬼,交 通管制,又是哪个大领导来视察)

4)如何定义操作原子操作

对于非long和double基本数据类型的“简单操作”都可以看作原子操作。for example:赋值和返回。在大多数系统中long和double都占据8个字节,操作系统或者JVM(java虚拟机)很可能会将写入和读取操作分离成两个独立的操作来执行,这就产生一个读取和写入过程中的上下文切换,从而导致多线程看到不正确的情况。

自增,自减非原子操作,一共三步(两次内存访问,一次寄存器修改)

比如:x++;(没有招,兄弟牺牲点性能吧)

mov        eax,dword ptr [x]
add eax,1
mov dword ptr [x],eax

  

二、操作系统之上的实现

    讲点没有用,有大咖测试发现,在linux上进程和线程的效率差不多,window上进程和线程效率差距很大。

1.window上多线程同步和互斥操作

分别有四种方式:临界区(CriticalSection)、互斥对象(Mutex)、信号量(Semaphore)、事件对象(Event)。

1)临界区:

使用函数:

CRITICAL_SECTION CriticalSection;
InitializeCriticalSection(&CriticalSection);
EnterCriticalSection(&CriticalSection);
LeaveCriticalSection(&CriticalSection);
DeleteCriticalSection(&CriticalSection);

write an example:

#include <string>
#include <iostream>
#include < process.h>
#include <windows.h>
using namespace std; CRITICAL_SECTION g_cs; unsigned __stdcall threadFun(void *param)
{
EnterCriticalSection(&g_cs);
cout<<*(string*)(param)<<endl;
LeaveCriticalSection(&g_cs);
return 1;
} int main()
{
InitializeCriticalSection(&g_cs); HANDLE hth1, hth2, hth3;
string s1 = "first", s2 = "second", s3= "third"; hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL);
hth2 = (HNADLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL);
hth3 = (HNADLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL); WaitForSingleObject(hth1, INFINITE);
WaitForSingleObject(hth2, INFINITE);
WaitForSingleObject(hth3, INFINITE); CloseHandle(hth1);
CloseHandle(hth2);
CloseHandle(hth3); DeleteCriticalSection(&g_cs);
return 0;
}

2) 互斥对象(Mutex)

引入了对象互斥对象的概念,来保证共享数据操作的完整性,每个对象对应于一个可称为互斥锁的标记,用这个标记用来保证在任一时刻,只要有一个线程访问该对象,操作接口:

CreateMutex
OpenMutex
ReleaseMutex

write an example:

#include<string>
#include<iostream>
#include<process.h>
#include<windows.h>
using namespace std; HANDLE hmu; unsined __stdcall threadFun(void *param)
{
WaitForSingleObject(hmu, INFINITE);
cout<<*(string*)(param)<<endl;
ReleaseMutex(hmu);
return 1;
} int main()
{
hmu = CreateMutex(NULL, FALSE, NULL);
HANDLE hth1, hth2, hth3;
string s1 = "first", s2 = "second", s3 = "third"; hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s1, 0, NULL);
hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s2, 0, NULL);
hth3 = (HANDLE)_beginthreadex(NULL, 0, threadFun, &s3, 0, NULL); WaitForSingleObjec(hth1, INFINITE);
WaitForSingleObjec(hth2, INFINITE);
WaitForSingleObject(hth3, INFINITE); CloseHandle(hth1);
CloseHandle(hth2);
CloseHandle(hth3); CloseHandle(hmu); 
         return 0; }

3)信号量(Semaphore)

信号量,别称信号灯。负责协调各个线程,保证正确合理的使用公共资源。

window环境接口:

CreateSemaphore
OpenSemaphore
ReleaseSemaphore

write an example:

#include <string>
#include <iostream>
#include <process.h>
#include <windows.h> using namespace std; HANDLE hsem1, hsem2, hsem3; usigned __stdcall threadFunA(void *)
{
for(int i = 0; i < 10; i++)
{
WaitForSingleObject(hsem1, INFINITE);
cout<<"A";
ReleaseSemaphore(hsem2, 1, NULL);
}
return 1;
} unsigned __stdcall threadFunB(void*)
{
for(int i = 0; i < 10; i++)
{
WaitForSingleObject(hsem2, INFINITE);
cout<<"B";
ReleaseSemaphore(hsem2, 1, NULL);
}
return 1;
} unsigned __stdcall threadFunC(void*)
{
for(inti =0; i < 10; i++)
{
WaitForSingleObject(hsem3, INFINITE);
cout<<"C";
ReleaseSemphore(hsem1, 1, NULL);
}
return 1;
} int main()
{
hsem1 = CreateSemaphore(NULL, 1, 1, NULL);
hsem2 = CreateSemaphore(NULL, 0, 1, NULL);
hsem3 = CreateSemaphore(NULL, 0, 1, NULL); HANDLE hth1, hth2, hth3; hth1 = (HANDLE)_beginthreadex(NULL, 0, threadFunA, NULL, 0, NULL);
hth2 = (HANDLE)_beginthreadex(NULL, 0, threadFunB, NULL, 0, NULL);
hth3 = (HANDLE)_beginthreadex(NULL, o, threadFunC, NULL, 0, NULl); WaitForSingleObject(hth1, INFINITE);
WaitForSingleObject(hth2, INFINITE);
WaitForSingleObject(hth3, INFINITE); CloseHandle(hth1);
CloseHandle(hth2);
CloseHandle(hth3);
CloseHandle(hsem1);
CloseHandle(hsem2);
CloseHandle(hsem3);
return 0;
}
 

4) 事件对象(Event)

windows事件操作

CreateEvent
OpenEvent
PulseEvent
ResetEvent
SetEvent

主线程一般可以这样写:

CreateEvent(/*...*/);    // 创建事件对象
SetEvent(/*...*/); // 设置信号
WaitForMultiObjects(hThread, /*...*/); // 等待线程结束
CloseHandle(/*...*/); // 关闭线程句柄

而被启动的线程一般要等待某个事件再进行操作:

while(1){
WaitForSingleObject(/*...*/); // 等待事件
/*...*/
}

总结:

1. 使用频率,信号量 > 互斥对象 > 临界区 > 事件对象

2. windows编程中有两种创建线程的方法,一种为CreateThread(),另一种为_beginthreadex(),当如果代码使用标准C运行库,建议使用后者。 (window API的好)

2.linux  c 多线程编程

无非也是锁机制,条件变量,读写锁,在linux编程中多线程和多进程差不多,无非API改改名,不做太多探讨。

write  simple an example:

#include"../apue.h"
int pos=0;
char *list[10]={"person1","person2","person3","person4","person5",
"person6","person7","person8","person9","person10"}; pthread_mutex_t mutex;
void *func(void *p)
{
int a = *(int*)p;
for(;;)
{
pthread_mutex_lock(&mutex);
if(pos>=10)
{
break;
}
printf("%d号咨询美女为%s服务\n",a,list[pos]);
pos++;
pthread_mutex_unlock(&mutex);
sleep(8);//模拟服务时间
}
pthread_mutex_unlock(&mutex);
}
int main()
{
int i;
int a=1,b=2,c=3;
pthread_t id1,id2,id3;
pthread_mutex_init(&mutex,NULL);
pthread_create(&id1,NULL,func,&a);
pthread_create(&id2,NULL,func,&b);
pthread_create(&id3,NULL,func,&c); pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
while(1);
}

  参考:网上的博客 (集百家之所长,补自己之所短)

《UNIX环境高级编程》这本书看完了,验证一遍,linux基本操作都应该没啥问题。

多线程的些许理解(平台x86,具体考虑linux,windows)的更多相关文章

  1. .NET Core多平台开发体验[3]: Linux (Windows Linux子系统)

    如果想体验Linux环境下开发和运行.NET Core应用,我们有多种选择.一种就是在一台物理机上安装原生的Linux,我们可以根据自身的喜好选择某种Linux Distribution,目前来说像R ...

  2. iOS安全些许经验和学习笔记

    http://bbs.pediy.com/showthread.php?t=209014 标题: [原创]iOS安全些许经验和学习笔记作者: MonkeyKey时间: 2016-03-30,16:32 ...

  3. 用python参加Kaggle的些许经验总结(收藏)

    Step1: Exploratory Data Analysis EDA,也就是对数据进行探索性的分析,一般就用到pandas和matplotlib就够了.EDA一般包括: 每个feature的意义, ...

  4. twisted高并发库transport函数处理数据包的些许问题

    还是在学校时间比较多, 能够把时间更多的花在学习上, 尽管工作对人的提升更大, 但是总是没什么时间学习, 而且工作的气氛总是很紧凑, 忙碌, 少了些许激情吧.适应就好了.延续着之前对twisted高并 ...

  5. 微软自然语言理解平台LUIS:从零开始,帮你开发智能音箱

    今年微软开发者大会Build 2017上展示了一款Invoke智能音箱,受到了媒体和大众的广泛关注.近两年,不少大公司纷纷涉足该领域,使得智能音箱逐渐成为一款热门的人工智能家用电器.智能音箱的兴起也改 ...

  6. C# 多线程编程第一步——理解多线程

    一.进程.线程及多线程的概念 什么是多线程呢?不理解. 那什么是线程呢?说到线程就不得不说说进程.我在网上搜索也搜索了一些资料,大部分所说的进程其实是很抽象的东西.通俗的来讲,进程就是一个应用程序开始 ...

  7. Android CodeReview 些许总结

    CodeReview些许总结 1:使用Handler的时候,使用handler.post(Runnable);,hanler与类尽量保持弱引用关系,或者使用静态的handler对象 public Ha ...

  8. SAAS云平台搭建札记: (二) Linux Ubutu下.Net Core整套运行环境的搭建

    最近做的项目,由于预算有限,公司决定不采购Windows服务器,而采购基于Linux的服务器. 一般的VPS服务器,如果使用Windows系统,那么Windows Server2012\2016安装好 ...

  9. Confluence5.8协作平台软件安装(Linux)

    Confluence5.8协作平台软件安装(Linux) 一.简介 Confluence是一个专业的企业知识管理与协同软件,也可以用于构建企业wiki.使用简单,但它强大的编辑和站点管理特征能够帮助团 ...

随机推荐

  1. ORA-00600: internal error code, arguments: [kdBlkCheckError]

    ORA-00600: internal error code, arguments: [kdBlkCheckError] Table of Contents 1. 现象 2. 分析 3. 故障处理 1 ...

  2. Oracle安装过程中Oracle Database Configuration Assistant失败问题解决

    今天在安装Oracle的过程中出现了oracle Database Configuration Assistant问题,我解决该问题的方法是将我的计算机用户名更改了一下,改成了pc1.之后再在orac ...

  3. 数据库高级数据库学习--上机练习7(Transact-SQL 函数定义和调用)

    上机练习7 在Transact SQL中,有一类特殊的自定义函数,其返回值为一张表,该类自定义函数被称作内嵌(联)表值函数,其基本语句格式如下: CREATE FUNCTION函数名称[( {@参数名 ...

  4. Goland 开发插件安装

    goland 是一款非常优秀的开发工具,默认打开后,发白的开发界面,也是异常刺眼.但是 Goland 为我们准备了很多插件,要优先安装这些插件,打造适合自己的开发界面. 我自己的设置的主题界面如下: ...

  5. LeetCode.1122-相对排序数组(Relative Sort Array)

    这是小川的第393次更新,第427篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第258题(顺位题号是1122).给定两个数组arr1和arr2,arr2中的元素是不同的 ...

  6. 史上最简单的CentOS7破解密码方法,有图有真相

    #############破解CentOS7密码详细过程 一.开机重启,按任意键停住,有时没停住,是鼠标键未在服务器中,点一下就好     二.按e键进入单用户模式     三.在UTF-8后面加上i ...

  7. TCP协议基础(一)

    TCP为应用层提供的服务 提供进程和进程之间的通信 答: 怎么区分服务目的主机上的哪个进程呢? 通过提供端口号(well-known port), 如Telnet 23,DNS 53, HTTP 80 ...

  8. Angular build编译内存溢出"JavaScript heap out of memory"的解决办法

    关于最近使用angular build编译打包的时候,遇到内存溢出的突发情况,做一个简单记录 编译报错如下↓↓↓ 报错信息很直观地指出是内存溢出了.是什么导致了内存溢出呢?其根本原因在于 nodejs ...

  9. DataGridView中的ComboboxCell报了System.ArgumentException:DagaGridViewComboBoxCell值无效错误

    原因是初始化的时候给ComboboxCell绑定了一系列的值,但是真正赋值的时候却给了一个不在那一系列值范围中的值,所以就报了这个错 在开发的时候难免会因为数据的问题出现这个问题,为了不让系统崩掉,就 ...

  10. [转贴]Linux内核LTS长期支持版生命周期

    Linux内核LTS长期支持版生命周期 https://blog.51cto.com/dangzhiqiang/1894026 搞不懂长期支持版本的特点和区别. 党志强关注0人评论4371人阅读201 ...