用C++11学习多线程
2015-11-15 16:57
351 查看
有了线程,多核才有了真正意义上的并发。在单核时代,所谓的并发只是伪并发,类似于事件轮询的方式。只是轮询的周期比较快,所以对于我们人类来说看上去和真的并发没什么区别。如果把主人公换成复仇者联盟2上的快银,在他的世界观里。。。呵呵
并发毫无疑问能提高工作效率,但也肯定会有一些问题需要注意。很明显的一个问题就是,对于共享资源的访问。因为线程之间是栈私有,堆共有的。对于局部变量不会有什么问题,但如果两个线程同时访问一个共享变量,这个时候就需要注意了。为了处理并发带来的隐患,我们发明了互斥量,信号量,条件变量,屏障等概念来处理线程对共享资源的访问。
在只有进程的年代,我们貌似只能通过fork之类的方式来复制一个进程,进程之间貌似没有同步的概念。进程之间只有通信。对于Unix IPC和网络编程,我肯定推荐Richard的著作,APUE和Unix网络编程。他老人家已经于1999年去世了,希望在天堂一切都好吧。
对于并发,不同平台有不同的实现。Windows上有Win32的API供你调用,Linux上也有一套自己的接口。好在POSIX的出现为我们编写可移植的程序提供了方便。同时C++11中以提供了一套跨平台的线程库。具体看见:传送门
用C++11写一个多线程的小程序:
程序就是上面这样,很简单。创建两个线程,每个线程各有一条打印语句。其中第二个线程是传引用。多运行几次程序,你就会发现两个线程的打印语句的执行顺序是不确定的,原因完全是因为处理器的调度。
代码也没什么好说的,只是要注意
这是一个被删除的函数,也就是说不支持拷贝赋值。
程序的功能如下:启动一个线程,就报一个数。可见于某网站统计用多少人登录吧。这里主要用到了原子操作。
打印结果见下图。是没有错误的,不过看官可以试一下不用原子操作。也就是最野性的
结果肯定不正确。理由也很简单:自加操作的汇编代码有三步:
除了原子操作,互斥量和信号量都能用来应对这种情况。所谓至于为什么叫原子操作,类比我们物理中的原子啦。是不可分割的最小单位,编程中的原子性是指。一个操作要么做完,要么不做。比如你像朋友打钱,打到一半断电了。。。这时候怎么办?
数据库中也有类似的概念,貌似叫做事务性。
运行一下很明显就能看到多线程的效果。
并发毫无疑问能提高工作效率,但也肯定会有一些问题需要注意。很明显的一个问题就是,对于共享资源的访问。因为线程之间是栈私有,堆共有的。对于局部变量不会有什么问题,但如果两个线程同时访问一个共享变量,这个时候就需要注意了。为了处理并发带来的隐患,我们发明了互斥量,信号量,条件变量,屏障等概念来处理线程对共享资源的访问。
在只有进程的年代,我们貌似只能通过fork之类的方式来复制一个进程,进程之间貌似没有同步的概念。进程之间只有通信。对于Unix IPC和网络编程,我肯定推荐Richard的著作,APUE和Unix网络编程。他老人家已经于1999年去世了,希望在天堂一切都好吧。
对于并发,不同平台有不同的实现。Windows上有Win32的API供你调用,Linux上也有一套自己的接口。好在POSIX的出现为我们编写可移植的程序提供了方便。同时C++11中以提供了一套跨平台的线程库。具体看见:传送门
用C++11写一个多线程的小程序:
#include <iostream> #include <thread> #include <cassert> using namespace std; //calc the Nth items of Fib series void f1(int n){ assert(n>0); for(int i=0;i<5;i++){ cout<<"thread f1 is running."<<endl; ++n; } } void f2(int &n){ assert(n>=0); for(int i=0;i<5;i++){ cout<<"thread f2 is running."<<endl; ++n; } } int main(void){ int n=0; std::thread t1(f1,n+1); std::thread t2(f2,std::ref(n)); t1.join(); t2.join(); cout<<"At the last the n's value is: "<<n<<endl; return 0; }
程序就是上面这样,很简单。创建两个线程,每个线程各有一条打印语句。其中第二个线程是传引用。多运行几次程序,你就会发现两个线程的打印语句的执行顺序是不确定的,原因完全是因为处理器的调度。
代码也没什么好说的,只是要注意
thread(const thread&) = delete;
这是一个被删除的函数,也就是说不支持拷贝赋值。
例子2 线程报数
#include <iostream> #include <thread> #include <mutex> #include <vector> #include <atomic> using namespace std; atomic<int> login_count; int thread_num=50; //创建线程数 int TIMES=20; //查看次数 void f(void){ login_count.fetch_add(1,std::memory_order_relaxed); Sleep(50); } int main(int argc,char **argv){ thread pool[thread_num]; while(TIMES-->0){ login_count=0; for(int i=0;i<thread_num;i++){ thread t(f); pool[i]=std::move(t); } //wait every thread finish his task for(int i=0;i<thread_num;i++) pool[i].join(); cout<<"50 users login.The output is: "<<login_count<<endl; } return 0; }
程序的功能如下:启动一个线程,就报一个数。可见于某网站统计用多少人登录吧。这里主要用到了原子操作。
login_count.fetch_add(1,std::memory_order_relaxed);
打印结果见下图。是没有错误的,不过看官可以试一下不用原子操作。也就是最野性的
longin_count++:
结果肯定不正确。理由也很简单:自加操作的汇编代码有三步:
1:move data from memory to register
2:add the data in register
3:move data back to memory
所以如果第一个线程执行到在寄存器中加1时被CPU挂起,此时运行第二个线程。。。。。除了原子操作,互斥量和信号量都能用来应对这种情况。所谓至于为什么叫原子操作,类比我们物理中的原子啦。是不可分割的最小单位,编程中的原子性是指。一个操作要么做完,要么不做。比如你像朋友打钱,打到一半断电了。。。这时候怎么办?
数据库中也有类似的概念,貌似叫做事务性。
java version
import java.util.concurrent.*; public class my_runnable implements Runnable{ int count=1,number; //constructor public my_runnable(int number){ this.number=number; } public void run(){ while(true){ System.out.println("thread ID is: " + number+" current number is: " + count); if(++count==6) return ; } } public static void main(String[] args){ for(int i=0;i<2;i++) new Thread(new my_runnable(i+1)).start(); } }
运行一下很明显就能看到多线程的效果。
相关文章推荐
- C#线程间不能调用剪切板的解决方法
- C#线程同步的三类情景分析
- C#获取进程或线程相关信息的方法
- C#停止线程的方法
- C#子线程更新UI控件的方法实例总结
- C#线程队列用法实例分析
- C++使用CriticalSection实现线程同步实例
- 基于C++实现的线程休眠代码
- 探究在C++程序并发时保护共享数据的问题
- VB读取线程、句柄及写入内存的API代码实例
- C#网络编程基础之进程和线程详解
- C#通过Semaphore类控制线程队列的方法
- C#多线程处理多个队列数据的方法
- C#实现线程安全的简易日志记录方法
- C#中线程同步对象的方法分析
- Nodejs实战心得之eventproxy模块控制并发
- ASP.NET线程相关配置
- 浅析linux环境下一个进程最多能有多少个线程
- 再谈JavaScript线程
- C#实现终止正在执行的线程