c 线程(平行世界)
我们已经知道如何使用进程来做一些事情了,然而 它并不是在什么地方都是最适合的。
我们看看进程的缺点是什么:

线程隆重登场
1. 如何创建线程
创建线程可以使用多种线程库,在此我们使用最流行的一种:POSIX线程库,也叫pthread。
假设有两个函数
void * dose_do(void * a) {
for (int i = ; i < ; i++) {
sleep();
puts("does_do");
}
return NULL;
}
void * dose_not(void * a) {
for (int i = ; i < ; i++) {
sleep();
puts("does_not");
}
return NULL;
}
这两个函数都返回了void指针,因为void指针可以指向存储器中任何数据类型的数据,线程函数的返回类必须是void *。
必须包含#include <pthread.h>头文件
我们使用pthread_create() 函数创建并运行一个线程,而且每个线程都需要把线程信息保存在一个pthread_t类型的数据中。
// new pthread
pthread_t t0;
pthread_t t1; if (pthread_create(&t0, NULL, dose_not, NULL) == -) {
error("无法创建线程t0");
}
if (pthread_create(&t1, NULL, dose_do, NULL) == -) {
error("无法创建线程t1");
}
上边的两个函数将会独立的在线程中运行,知道结束,但是我们需要知道这两个函数什么时候结束。
我们使用pthread_join()函数等待函数结束,他会接受线程函数的返回值,并保存在一个void *类型的数据中。
那么这个函数是如何得知线程结束的呢?当得到线程函数的返回值的时候,就表明线程函数结束了。这也是为什么线程函数必须要有返回值的原因。
void *result;
if (pthread_join(t0, &result) == -) {
error("无法回收线程t0");
}
if (pthread_join(t1, &result) == -) {
error("无法回收线程t1");
}
我们来看 全部代码
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h> // 错误处理函数
void error(char *msg) {
fprintf(stderr, "Error: %s %s", msg, strerror(errno));
exit();
} void * dose_not(void * a) { for (int i = ; i < ; i++) {
sleep();
puts("does_not");
} return NULL;
} void * dose_do(void * a) { for (int i = ; i < ; i++) {
sleep();
puts("does_do");
} return NULL;
} int main(int argc, const char * argv[]) { // new pthread
pthread_t t0;
pthread_t t1; if (pthread_create(&t0, NULL, dose_not, NULL) == -) {
error("无法创建线程t0");
}
if (pthread_create(&t1, NULL, dose_do, NULL) == -) {
error("无法创建线程t1");
} void *result;
if (pthread_join(t0, &result) == -) {
error("无法回收线程t0");
}
if (pthread_join(t1, &result) == -) {
error("无法回收线程t1");
} return ;
}
结果如下

再来看下边这段代码,我们有2000000瓶啤酒,开启20条线程,看最后剩余多少瓶?
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h> // 错误处理函数
void error(char *msg) {
fprintf(stderr, "Error: %s %s", msg, strerror(errno));
exit();
} int beers = ; void * drink_lots(void * a) { for (int i = ; i < ; i++) { beers = beers - ; } printf("剩余 %i 瓶啤酒 \n",beers); return NULL;
} int main(int argc, const char * argv[]) { // new pthread
pthread_t pthreads[]; printf("%i 瓶啤酒 \n",beers); for (int i = ; i < ; i++) { if (pthread_create(&pthreads[i], NULL, drink_lots, NULL) == -) {
error("无法创建线程");
}
} void *result; for (int i = ; i < ; i++) { if (pthread_join(pthreads[i], &result) == -) {
error("无法回收线程");
}
} return ;
}
运行结果

那么问题来了,为什么跟我们想要的结果不一样呢? 其实都点编程经验的人都知道,线程是不安全的。
要想解决这样的问题就要使用互斥锁
2. 用互斥锁保护线程
互斥锁必须对所有可能发生冲突的线程可见,也就是说它是一个全局变量。
创建:
pthread_mutex_t beers_lock = PTHREAD_MUTEX_INITIALIZER;
加锁
pthread_mutex_lock(&beers_lock);
解锁
pthread_mutex_unlock(&beers_lock);
我们修改上边的关于啤酒的函数为
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h> // 错误处理函数
void error(char *msg) {
fprintf(stderr, "Error: %s %s", msg, strerror(errno));
exit();
} pthread_mutex_t beers_lock = PTHREAD_MUTEX_INITIALIZER; int beers = ; void * drink_lots(void * a) { for (int i = ; i < ; i++) { pthread_mutex_lock(&beers_lock);
beers = beers - ;
pthread_mutex_unlock(&beers_lock);
} printf("剩余 %i 瓶啤酒 \n",beers); return NULL;
} int main(int argc, const char * argv[]) { // new pthread
pthread_t pthreads[]; printf("%i 瓶啤酒 \n",beers); for (int i = ; i < ; i++) { if (pthread_create(&pthreads[i], NULL, drink_lots, NULL) == -) {
error("无法创建线程");
}
} void *result; for (int i = ; i < ; i++) { if (pthread_join(pthreads[i], &result) == -) {
error("无法回收线程");
}
} return ;
}
运行结果如下

每个线程中循环结束后才会打印结果,也就是说当循环完之后打印的结果就是那个时间点还剩多少瓶啤酒。
线程的知识和运用先简单介绍到这,后续会增加实战的内容。
c 线程(平行世界)的更多相关文章
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- [高并发]Java高并发编程系列开山篇--线程实现
Java是最早开始有并发的语言之一,再过去传统多任务的模式下,人们发现很难解决一些更为复杂的问题,这个时候我们就有了并发. 引用 多线程比多任务更加有挑战.多线程是在同一个程序内部并行执行,因此会对相 ...
- 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- Java 线程
线程:线程是进程的组成部分,一个进程可以拥有多个线程,而一个线程必须拥有一个父进程.线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不能拥有系统资源.它与父进程的其他线程共享该进程的所有资 ...
- C++实现线程安全的单例模式
在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式.单例模式分为懒汉模式,跟饿汉模式两种. 首先给出饿汉模式的实现 template <class T> class sing ...
- 记一次tomcat线程创建异常调优:unable to create new native thread
测试在进行一次性能测试的时候发现并发300个请求时出现了下面的异常: HTTP Status 500 - Handler processing failed; nested exception is ...
- Android线程管理之ThreadLocal理解及应用场景
前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...
- C#多线程之线程池篇3
在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...
- C#多线程之线程池篇2
在上一篇C#多线程之线程池篇1中,我们主要学习了如何在线程池中调用委托以及如何在线程池中执行异步操作,在这篇中,我们将学习线程池和并行度.实现取消选项的相关知识. 三.线程池和并行度 在这一小节中,我 ...
随机推荐
- Storm如何保证可靠的消息处理
作者:Jack47 PS:如果喜欢我写的文章,欢迎关注我的微信公众账号程序员杰克,两边的文章会同步,也可以添加我的RSS订阅源. 本文主要翻译自Storm官方文档Guaranteeing messag ...
- ASP.NET Core 1.1 简介
ASP.NET Core 1.1 于2016年11月16日发布.这个版本包括许多伟大的新功能以及许多错误修复和一般的增强.这个版本包含了多个新的中间件组件.针对Windows的WebListener服 ...
- JavaScript 字符串实用常操纪要
JavaScript 字符串用于存储和处理文本.因此在编写 JS 代码之时她总如影随形,在你处理用户的输入数据的时候,在读取或设置 DOM 对象的属性时,在操作 Cookie 时,在转换各种不同 Da ...
- gradle学习笔记(1)
1. 安装 (1) 下载最新gradle压缩包,解压到某处.地址是:Gradle web site: (2) 添加环境变量: 1) 变量名:GRADLE_HOM ...
- 实例操作JSONP原理
絮语:按这个步骤走,你就会明白JSONP是什么鬼. 1.工程目录: ng-mywork demo.html test.js 2.nginx的server配置 server { listen ; ser ...
- OpenCASCADE BRep Projection
OpenCASCADE BRep Projection eryar@163.com 一网友发邮件问我下图所示的效果如何在OpenCASCADE中实现,我的想法是先构造出螺旋线,再将螺旋线投影到面上. ...
- windows环境下sublime的nodejs插件详细安装图解
前面的话 搜索了好多文档后,才成功地安装了sublime text3的nodejs插件.为了存档,也为了方便有同样需求的朋友,将其安装过程详细记录如下 安装nodejs 虽然nodejs官网提供了 ...
- C#委托异步调用
参考页面: http://www.yuanjiaocheng.net/webapi/mvc-consume-webapi-get.html http://www.yuanjiaocheng.net/w ...
- .NET 基础 一步步 一幕幕[面向对象之对象和类]
对象和类 本篇正式进入面向对象的知识点简述: 何为对象,佛曰:一花一世界,一木一浮生,一草一天堂,一叶一如来,一砂一极乐,一方一净土,一笑一尘缘,一念一清静.可见"万物皆对象". ...
- C#开发微信门户及应用(39)--使用微信JSSDK实现签到的功能
随着微信开逐步开放更多JSSDK的接口,我们可以利用自定义网页的方式来调用更多微信的接口,实现我们更加丰富的界面功能和效果,例如我们可以在页面中调用各种手机的硬件来获取信息,如摄像头拍照,GPS信息. ...