TinyLab

Keep eyes on the star and feet on the ground.


  • 首页

  • 归档

  • 分类

  • 标签

每周一荐:foobar2000, 奇幻人生, 一个Flash小游戏

发表于 2010-12-12   |   分类于 每周一荐

Software:foobar2000

foobar2000是原Winamp工作组成员Peter Pawlowski 编写的一个专业数字音频播放器。据说是因为他不满于Winamp 2.x的插件体系架构和更倾向于图形、皮肤的发展方向的Winamp3 。

foobar2000最值得关注的在于他良好的体系架构。

除了重要的音频管道以外,播放器所有功能部件均是模块化的。可视化的核心由一个插件提供,

即使使用了不同的接口界面。这种程序架构使它成为目前最自由化的音频播放软件,任何人都被允

许利用它的接口制作附加功能模块。

主要特性:

  1. 开放的组件体系结构允许第三方开发者来扩展播放器的功能
  2. 内置音频格式支持:WAV,AIFF,VOC,AU,SND, Ogg Vorbis,MPC,MP2,MP3,WavPack,Flac
  3. 通过官方插件支持的音频格式:MPEG-4 AAC,Ogg FLAC,Monkey’’’’s Audio,Speex,CDDA,SPC,各种MOD类型; 从RAR、ZIP压缩包里直接读取音频
  4. 在Windows NT上完整的Unicode支持
  5. 支持播放增益(ReplayGain)
  6. 低内存占用,有效处理大量的播放列表
  7. 高级文件信息处理能力(常规文件信息窗口和批量标签)
  8. 高度自定义播放列表显示
  9. 自定义快捷键
  10. 通过界面插件高度可自定义的界面
  11. 在BSD许可协议下大部分标准组件开源

可以很简洁,也可以很绚丽。强大的可定制能力不错!我喜欢!贴几张截图欣赏下:

foobar2000
foobar2000
foobar2000

Movie:Stranger Than Fiction

《Stranger Than Fiction : 奇幻人生》是由马克·福斯特执导的,讲述一个普遍的幻想–我们都感觉有个叙述者在跟随我们的人生,我们的心里有声音不断提醒我们做什么,怎么去做。本片名字是取自马克吐温的名句:真相比小说还要不可思议,因为文学作品不得不亦步亦趋于可能性,而现实则随心所欲的书写故事。(Truth is stranger than fiction,because fiction is obliged to stick to possibilities;truth isn’t)

税审计员哈洛德(威尔·法瑞尔)最近总觉得耳边总有一个女人(艾玛·汤普森)声音在念叨,就像是人们生活中的种种片段的旁白。而在他的生活过程中或是 即将经历的种种生活细节尽在旁白的“掌握”中,从刷牙、洗脸的各个步骤到穿衣的顺序最后到手表的摆放位置,对他的每一个细节,这个声音都了如指掌。 这令他惊诧不已。原来,声音来自一个深居简出的英国悲剧女作家凯伦,最近她正在倾力完成自己的最新作品。而她并不知道故事的主人公就生活在现实中,而其生活的际遇与她笔下的文字不谋而合,并且,她的这个小说的结局就是主人公哈洛德的悲惨离世。虚构与现实正在奇怪地交织纠缠着。知道这一切原委的哈洛德为了拯救自己的性命,只好求助于文学教授朱利斯·赫尔波特(达斯汀·霍夫曼),一同说服女作家改变小说的结局,而凯伦则面临着出版社的催促而逐渐地加快了写作的速度……

foobar2000

Game:athletics

没想到走个路都这么难!考验协调性的时刻到来了!可以试玩下看。一个很小的FLASH游戏,QWOP四个键,控制运动员的身体协调。最远跑了10m,真是汗颜!

Flash地址:http://www.foddy.net/Athletics.html

Reference

  1. http://www.foddy.net/Athletics.html
  2. http://www.foobar2000.org/
  3. http://www.foobar2000.com.cn/index.aspx

每周一荐: GoodReader, Machinarium, 悲怆交响曲

发表于 2010-12-04   |   分类于 每周一荐

好的东西,推荐给大家!希望大家每一天都有新的发现!

Software:GooReader

Google Books里有很多值得一读的好书,不过网页版用户体验做的确实是差了点,不仅有点卡还让你感觉不出是在读一本书。

好在有GooReader,这个Windows下的应用程序可以让你直接搜索Google Books里的图书,并在书架上直接阅读,带有绿色书签的书可以直接阅读,而红色标签的书则表示无法阅读全文(Google Books的限制)。打开一本书之后你可以缩放(不过不一定保证清晰,取决于Google的扫描质量),不过目前无法加书签也无法将一本书加入收藏。

GooReader有免费版和付费版,区别在于是否可以将图书存为PDF格式保存,目前仅有Windows版本,且需要装有.NET 3.51 SP1。

还可以将自己电脑里面的PDF格式的书籍,用GooReader来管理,非常方便!

GooReader
GoodReader

    

Game:Machinarium:机械迷城

《Machinarium:机械迷城》由捷克独立开发小组Amanita Design设计制作的作品,此作堪称以独特的水墨风格展现给玩家的是一款冒险解谜游戏,对于我这个不怎么喜欢玩游戏的人来说,它的确挺好玩的,不过因为休息时间比较少,每天也就玩一小会儿,不过真的很不错,推荐给大家。

游戏将采用传统点击式界面,和Samorost游戏相似,2D背景和人物,没有对白.不过,Machinarium游戏时间将更长更复杂,画面将由手画,而玩家有个小物品栏。 Machinarium中每个人都是机器人,包括我们的小英雄,他将对从 “Black Cap Brotherhood”来的坏人。本游戏在2009年独立游戏节上斩获了视觉艺术奖。

Machinarium

Music:第六交响曲(悲怆)

柴可夫斯基的第六交响曲(悲怆)大约在1893年八月末至九月间完成,为作者的代表作。柴科夫斯基自认为这部交响曲是他一生中最成功的作品,也是他最得意的杰作。本曲首演于同年的十月二十八日,六天之后,作者不幸染上霍乱,与世长辞。本曲终成为柴科夫斯基的“天鹅之歌”。

这首交响曲正如标题所示,强烈地表现出“悲怆”的情绪,这一点也就构成本曲的特色。柴科夫斯基音乐的特征,如旋律的优美,形式的均衡,管弦乐法的精巧等优点,都在本曲中得到深刻的印证,因此本曲不仅是柴科夫斯基作品中最著名、最杰出的乐曲之一,也是古今交响曲中第一流的精品。

本交响曲旨在描写人生的恐怖、绝望、失败、灭亡等,充满了悲观的情绪,而否定了一切肯定、享受人生的乐观情绪。作者在本曲中也刻意描写了人们为生活而奔忙的情景,但他揭示了一个永恒的真理——死亡是绝对的、无可避免的,而生活中的所有欢乐都是转瞬即逝的。作者所体现出的这类情绪,实际上反映的是在沙皇俄国末期,俄罗斯人民处于一种被压抑状况下的真实心态。

本曲虽属于标题音乐,但决不是针对某一特定事件或某一特殊个人的感情描写,只是以抽象手法表现人类共同具有的悲怆情绪而已。因此有的乐评家认为,本曲不应视为纯粹的标题音乐。

全曲共分为四个乐章:

第一乐章 慢板,转不很快的快板,b小调,4/4拍子,奏鸣曲形式。序奏为慢板,低音提琴以空虚的重音作为引子,由低音管在低音区演奏出呻吟般的旋律,其他乐器则如叹息般地继续。乐曲自开始就笼罩在一种烦躁不安的阴沉气氛中。主部的第一主题快速而富节奏感地奏出,给人以苦恼、不安和焦燥的印象。之后乐曲的速度旋即转成行板,第二主题哀愁而美丽,有如暂时抛却苦恼而沉入幻想中一般(片段1)。本乐章的终结部十分柔美、温和,旋律在平静的伴奏下伸展,形成谜一样的结尾。

第二乐章 温柔的快板,D大调,5/4拍子。自始自终 一贯单纯的色彩,其构想似乎来自俄罗斯民谣。5/4拍子的分配方式为, 各小节的前半部分为二拍,后半部分为三拍,形成了不安定而又稍快的音乐,全乐章呈现出昏暗、低迷的状态。主部的主要旋律具有舞蹈般的节奏,却又荡漾着一丝不安的空虚感(片段2)。

第三乐章 甚活泼的快板,G大调,4/4拍子,谐谑曲与进行曲混合而无发展部的奏鸣曲式。这一乐章的主要内容反映了人们四处奔忙、积极生活的景象,有人认为这一乐章体现出作者对过去的回忆。本乐章第一主题为谐谑曲式,轻快、活泼,与前两个乐章的主题形成对比(片段3)。 乐章的第二主题很像意大利南部的一种民族舞蹈音乐——塔兰泰拉舞曲,其主要旋律具有战斗般的感觉,但这一主题在进行曲般的旋律中,并没有明朗、快活的气息,反而呈现出一种悲壮感。 这一主题旨在表现人类的苦恼爆发时,所发泄出的反抗力量(片段4)。 此部分略经扩展后,再次出现诙谐曲主题而达到高潮。紧接着进行曲主题再现,乐章的终结部便在进行曲主题片断堆积的形态下强烈地结束。

第四乐章 终曲,哀伤的慢板,b小调,3/4拍,自由的三段体。 本乐章的主题极为沉郁、晦暗(一般交响曲的终曲都是最为快速、壮丽的乐章,而本交响曲正相反,充分强调了“悲怆”的主题),悲伤的旋律在两声圆号的衬托下显得更加凄凉(片段5)。 本乐章在无限凄寂当中结束。这一乐章正如本交响曲的标题,描写人生的哀伤、悲叹和苦恼,凄怨感人,有深沉的悲怆之美。

Refrence

  1. http://www.gooreader.com/
  2. http://www.hongxiao.com/mq0/c.beichuang/0.htm

Prototype模式去掉Clone方法

发表于 2009-10-12   |   分类于 设计模式

意图:

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

结构图:

结构图

Prototype的主要缺陷是每一个Prototype的子类都必须实现Clone操作,这很烦。
一般都这样实现:

1
2
3
4
5
 
Prototype* ConcretePrototype::Clone()
{
return new ConcretePrototype(*this);
}

现在想去掉这个重复的操作

结构图如下:

结构图

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

class PrototypeWrapper
{
public:
~PrototypeWrapper() {}
virtual Prototype* clone() = 0;
};

template <typename T>
class PrototypeWrapperImpl : public PrototypeWrapper
{
public:
PrototypeWrapperImpl()
{
_prototype = new T();
}
virtual Prototype* clone()
{
return new T(*_prototype);
}
private:
T* _prototype;
};

使用:

1
2
3

PrototypeWrapper* prototype = new PrototypeWrapperImpl<ConcretePrototype>();
Prototype* p = prototype->clone();

编译时断言和运行时断言

发表于 2009-08-26   |   分类于 Linux开发

通常为了检测一些条件,我们往往在程序里面加断言。一般只在DEBUG版有效,RELEASE版断言不生成任何代码。C++可以使用两种断言: 静态断言和动态断言,即就是运行期断言和编译期断言!顾名思义,运行期断言是在程序运行过程中判断指定的条件,若条件满足,万事OK,若断言失败,则程序给出提示然后被abort掉;编译期断言是在编译时候检查条件是否满足,不满足情况下,编译器给出错误提示(需要人为实现),只要条件不成立,程序是编译不过的。静态断言,BOOST库有实现(boost/static_assert.hpp),主要原理就是根据”sizeof(不完整类型)”会报错。动态断言在cassert库文件有实现。实现如下:

动态断言:(cassert)

1
2
3
4
5
6
7
8
9
10
11
12

#ifdef NDEBUG

// 不做任何处理
# define assert(expr)

#else

// __assert_failed 打印错误消息(包含表达式串,文件,所在行,所在函数名),然后abort()。
# define assert(expr) ((expr) ? 0 : __assert_failed(__STRING(expr), __FILE__, __LINE__, __PRETTY_FUNCTION__, 0))

#endif

静态断言:(boost/static_assert.hpp)

1
2
3
4
5
6
7
8
9
10
11
12
13

template <bool x> struct STATIC_ASSERTION_FAILURE;

template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };

template<int x> struct static_assert_test{};

#define BOOST_STATIC_ASSERT( B ) /
typedef ::boost::static_assert_test</
sizeof(::boost::STATIC_ASSERTION_FAILURE< (bool) (B) >)
> boost_static_assert_typedef_

// 当B为false时,sizeof(STATIC_ASSERTION_FAILURE<false>),STATIC_ASSERTION_FAILURE<false>)没有实现(不能实例化),为不完整类,编译器报错!

注意:和动态断言不同的是,静态断言可以在名称空间,类,函数,模板(函数模板和类模板)中使用,因为他用的是typedef。

静态断言的详细用法,查看:http://www.boost.org/doc/libs/1_39_0/doc/html/boost_staticassert.html

Kernel. EXPORT_SYMBOL解析

发表于 2009-04-01   |   分类于 Linux

Code Segment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

include/module.h:

struct kernel_symbol
{
unsigned long value;
const char *name;
};

/* For every exported symbol, place a struct in the __ksymtab section */
#define __EXPORT_SYMBOL(sym, sec) /
__CRC_SYMBOL(sym, sec) /
static const char __kstrtab_##sym[] /
__attribute__((section("__ksymtab_strings"))) /
= MODULE_SYMBOL_PREFIX #sym; /
static const struct kernel_symbol __ksymtab_##sym /
__attribute_used__ /
__attribute__((section("__ksymtab" sec), unused)) /
= { (unsigned long)&sym, __kstrtab_##sym }
#define EXPORT_SYMBOL(sym) /
__EXPORT_SYMBOL(sym, "")
#define EXPORT_SYMBOL_GPL(sym) /
__EXPORT_SYMBOL(sym, "_gpl")
#endif

Analysis:

  1. kernel_symbol: 内核函数符号结构体

    value: 记录使用EXPORT_SYMBOL(fun),函数fun的地址
    name: 记录函数名称(”fun”),在静态内存中

  2. EXPORT_SYMBOL(sym) :导出函数符号,保存函数地址和名称

宏等价于:(去掉gcc的一些附加属性,MODULE_SYMBOL_PREFIX该宏一般是””)

static const char __kstrtab_sym[] = "sym";
static const struct kernel_symbol __ksymtab_sym =
    {(unsigned long)&sym, __kstrtab_sym }
  1. gcc 附加属性

atrribute 指定变量或者函数属性。在此查看详细http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes。

__attribute((section(“section-name”)) var : 编译器将变量var放在section-name所指定的data或者bss段里面。

很容易看出:EXPORT_SYMBOL(sym)将sym函数的名称kstrtab_sym记录在,段名为”kstrtab_strings”数据段中。 将sym所对应的kernel_symbol记录在名为__ksymtab段中。

EXPORT_SYMBOL_GPL(sym) 和EXPORT_SYMBOL不同之处在于sym对应的kenel_symbol记录在__ksymtab_gpl段中。

深入Pthread(五):线程属性

发表于 2009-02-18   |   分类于 Linux开发

线程属性相关API

phtread_attr_t attr;
int pthread_attr_init(pthread_attr_t* attr);
int pthread_attr_destroy(pthread_attr_t* attr);
int pthread_attr_getdetachstate(pthread_attr_t* attr, int* detachstate);
int pthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate);

#ifdef _POSIX_THREAD_ATTR_STACKSIZE
int pthread_attr_getstacksize(pthread_attr_t* attr, size_t* stacksize);
int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize);
#endif

#ifdef _POSIX_THREAD_ATTR_STACKADDR
int pthread_attr_getstackaddr(pthread_attr_t* attr, void* stackaddr);
int pthread_attr_setstackaddr(pthread_attr_t* attr, void** stackaddr); 
#endif

线程属性

POSIX定义的线程属性有:可分离状态(detachstate), 线程栈大小(stacksize),线程栈地址( stackaddr),作用域(scope), 继承调度(inheritsched), 调度策略(schedpolicy)和调度参数( schedparam)。 有些系统并不支持所有这些属性,使用前注意查看系统文档。

但是所有Pthread系统都支持detachstate属性,该属性可以是PTHREAD_CREATE_JOINABLE或PTHREAD_CREATE_DETACHED,默认的是joinable的。拥有joinable属性的线程可以被另外一个线程等待,同时还可以获得线程的返回值,然后被回收。而detached的线程结束时,使用的资源立马就会释放,不用其他线程等待。

线程stacksize属性移植性不是很好,若你的系统定义了_POSIX_THREAD_ATTR_STACKSIZE ,才可以调用api设定线程堆栈大小。Pthreads规定线程堆栈大小必须大于等于PTHREAD_STACK_MIN。

线程stackaddr属性移植性相当不好,若系统定义了_POSIX_THREAD_ATTR_STACKADDR,才可以调用api设定线程堆栈地址,指定一块内存区域,这块内存区域大小至少是PTHREAD_STACK_MIN。机器堆栈向上增长的,必须指定为低地址;机器堆栈向下增长的,必须指定为高地址。这个属性,最好不要用。

例程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

#include <pthread.h>
#include "error.h"
#include <limits.h>

pthread_attr_t attr;


void* thread_routine(void* arg)
{
sleep(1);
#ifdef _POSIX_THREAD_ATTR_STACKSIZE
size_t stacksize;
int status = pthread_attr_getstacksize(&attr, &stacksize);
printf("[stacksize:%lu]thread routine is running..../n",stacksize);
#endif

return NULL;
}

int main()
{
pthread_t pid;
int status;
size_t stacksize;
status = pthread_attr_init(&attr);
if(status != 0)
ERROR_ABORT(status,"Init attr");

status = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if(status != 0)
ERROR_ABORT(status, "Set detachstate");

#ifdef _POSIX_THREAD_ATTR_STACKSIZE
status = pthread_attr_getstacksize(&attr, &stacksize);
if(status != 0)
ERROR_ABORT(status, "Get stacksize");
printf("Original thread size:%lu/n", stacksize);

status = pthread_attr_setstacksize(&attr, 2*PTHREAD_STACK_MIN);
if(status != 0)
ERROR_ABORT(status, "Set stacksize");
#endif

status = pthread_create(&pid, &attr, thread_routine, NULL);
if(status !=0 )
ERROR_ABORT(status, "Create thread");

status = pthread_attr_destroy(&attr);
if(status != 0)
ERROR_ABORT(status, "Destroy attr");

printf("Main thread is over.../n");
pthread_exit(NULL);
}

深入Pthread(四):一次初始化-pthread_once_t

发表于 2009-02-16   |   分类于 Linux开发

用到的API:

pthread_once_t once_control = PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t* once_control, void (*init_routine)(void));

有些事需要一次且仅需要一次执行。通常当初始化应用程序时,可以比较容易地将其放在main函数中。但当你写一个库时,就不能在main里面初始化了,你可以用静态初始化,但使用一次初始化(pthread_once_t)会比较容易些。

例程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

#include <pthread.h>
#include "errors.h"


pthread_once_t once_block = PTHREAD_ONCE_INIT;
pthread_mutex_t mutex;


/*This is the one-time initialization routine. It will be
* called exactly once, no matter how many calls to pthread_once
* with the same control structure are made during the course of
* the program.
*/

void once init routine (void)
{
int status;
status = pthread_mutex_init (&mutex, NULL);
if (status != 0)
err_abort (status, "Init Mutex");
}

/* Thread start routine that calls pthread_once.
*/
void *thread routine (void *arg)
{
int status;
status = pthread_once (&once_block, once_init_routine);
if (status != 0)
err_abort (status, "Once init");
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
printf ("thread routine has locked the mutex./n");

status = pthread_mutex_unlock (&mutex);
if (status ! = 0)
err_abort (status, "Unlock mutex");
return NULL;
}

int main (int argc, char *argv[])
{
pthread_t thread_id;
char *input, buffer[64];
int status;
status = pthread_create (&thread_id, NULL, thread_routine, NULL);
if (status != 0)
err_abort (status, "Create thread");
status = pthread_once (&once_block, once_init_routine);
if (status != 0)
err_abort (status, "Once init");
status = pthread_mutex_lock (&mutex);
if (status != 0)
err_abort (status, "Lock mutex");
printf ("Main has locked the mutex./n");
status = pthread_mutex_unlock (&mutex);
if (status != 0)
err_abort (status, "Unlock mutex");
status = pthread_join (thread_id, NULL);
if (status != 0)
err_abort (status, "Join thread");
return 0;
}

深入Phtread(三):线程的同步-Condition Variables

发表于 2009-02-12   |   分类于 Linux开发

继续昨天的线程同步,条件变量(Condition Variables)是用于线程间,通信共享数据状态改变的机制。

简介

当线程互斥地访问一些共享的状态时,往往会有些线程需要等到这些状态改变后才应该继续执行。如:有一个共享的队列,一个线程往队列里面插入数据,另一个线程从队列中取数据,当队列为空的时候,后者应该等待队列里面有值才能取数据。而共享数据(队列)应该用mutex来保护,为了检查共享数据的状态(队列是否为空),线程必须先锁定mutex,然后检查,最后解锁mutex。

问题出来了:当另外一个线程B锁定mutex后,往队列里面插入了一个值,B并不知道A在等着它往队列里面放入一个值。,线程A(等待状态改变)一直在运行,线程B可能已经检查过队列是空的,并不知道队列里已经有值了,所以一直阻塞着自己。为了解决这样的问题引入了条件变量机制。线程B等待于一个条件变量,当线程A插入了一个值后,signal或broadcast这个条件变量,通知线程B状态已改变,A发现条件变量被signaled了,就继续执行。就这样,当一个线程改变共享数据状态后,可以及时通知那些等待于该状态的线程。图示下:

条件变量

中间的矩形代表条件变量,当线程线位于矩形内,表示线程等待该条件变量。位于中心线下下方,则表示signal了该条件变量。

开始线程1 signal 了条件变量,由于没有其他线程等待于该条件变量,所以没什么效果。然后,线程1和线程2先后等待该条件变量,过了一会,线程3 signal了条件变量,线程3的信号解除了线程1的阻塞。然后,线程3等待该条件变量。最后线程1 broadcast了该条件变量,同时解除了等待于条件变量的线程1和线程2。

条件变量的创建和销毁

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t* cond, pthread_condattr_t* condattr);
int pthread_cond_destroy(pthread_cond_t* cond);

和互斥量一样,可以动态创建和静态创建。

静态创建:条件变量声明为extern或static变量时。

例程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#include <pthread.h>
#include "error.h"

typedef struct my_struct_tag
{
pthread_mutex_t mutex;
pthread_cond_t cond;
int value;
} my_struct_t;

my_struct_t data = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};

int main()
{
return 0;
}

动态创建:一般情况下,条件变量要和它的判定条件定义在一起,此时若包含该条件变量的数据动态创建了,则条件变量也需要动态创建,不过记得不用时用pthread_cond_destroy销毁。

例程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

#include <pthread.h>
#include "error.h"

typedef struct my_struct_tag
{
pthread_mutex_t mutex;
pthread_cond_t cond;
int value;
} my_struct_t;

int main()
{
my_struct_t* data;
data = (my_struct_t*)malloc(sizeof(my_struct_t));
if(data == NULL)
ERROR_ABORT(errno,"Allocate structure");

int status;
status = pthread_mutex_init(&data->mutex, NULL);
if(status != 0)
ERROR_ABORT(status, "Initial mutex");
status = pthread_cond_init(&data->cond, NULL);
if(status != 0)
ERROR_ABORT(status, "Initial condition");

/* .... */

status = pthread_cond_destroy(&data->cond);
if(status != 0)
ERROR_ABORT(status, "Destroy cond");
status = pthread_mutex_destroy(&data->mutex);
if(status != 0)
ERROR_ABORT(status, "Destroy mutex");

free(data);

return 0;
}

等待条件变量

int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex);
int pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, struct timespec* expiration);

条件变量与互斥量一起使用,调用pthread_cond_wait或pthread_cond_timedwait时,记得在前面锁定mutex,尽可能多的判断判定条件。上面提到的两个等待条件变量的函数,显示解锁mutex,然后阻塞线程等待状态改变,等待的条件变量signaled后,锁定mutex,返回。记着,这两个函数返回时,mutex一定是锁定的。

多个条件变量可以共享一个互斥变量,相反则不成立。

例程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

#include <pthread.h>
#include <time.h>
#include "error.h"
#include <errno.h>

typedef struct my_struct_tag
{
pthread_mutex_t mutex;
pthread_cond_t cond;
int value;
} my_struct_t;

my_struct_t data = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};

int hibernation = 1;

void* wait_thread(void* arg)
{
int status;
sleep(hibernation);

status = pthread_mutex_lock(&data.mutex);
if(status != 0)
ERROR_ABORT(status, "Lock mutex");

data.value = 1;
status = pthread_cond_signal(&data.cond);
if(status != 0)
ERROR_ABORT(status, "Singal cond");

status = pthread_mutex_unlock(&data.mutex);
if(status != 0)
ERROR_ABORT(status, "Unlock mutex");

return NULL;
}

int main(int argc, char* argv[])
{
pthread_t tid;
int status;
struct timespec timeout;

if(argc > 1)
hibernation = atoi(argv[1]);

status = pthread_create(&tid, NULL, wait_thread, NULL);
if(status != 0)
ERROR_ABORT(status, "Create wait thread");

timeout.tv_sec = time(NULL) + 2;
timeout.tv_nsec = 0;

status = pthread_mutex_lock(&data.mutex);
if(status != 0)
ERROR_ABORT(status, "Lock mutex");

while(data.value == 0)
{
status = pthread_cond_timedwait(&data.cond, &data.mutex, &timeout);
if(status == ETIMEDOUT)
{
printf("Condition wait timed out./n");
break;
}else
if(status != 0)
ERROR_ABORT(status, "timewait");
}

if(data.value != 0)
printf("Condition wa signaled!/n");

status = pthread_mutex_unlock(&data.mutex);
if(status != 0)
ERROR_ABORT(status, "Unlock mutex");
}

唤醒等待条件变量的线程

int pthread_cond_signal(pthread_cond_t* cond);
int pthread_cond_broadcast(pthread_cond_t* cond);

一但有线程由于某些判定条件(predicate)没满足,等待条件变量。我们就有必要当条件满足时,发送信号去唤醒这些线程。

注意:broadcast通常很容易被认为是signal的通用版,其实不能这样理解,准确一点应该说,signal是broadcast的优化版。具体区别不大,但signal效率较broadcast高些。但你不确信有几个线程等待条件变量时用broadcast(When in doubt, broadcast!)。

例程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177

#include "error.h"
#include <pthread.h>
#include <time.h>
#include <string.h>
#include <errno.h>

typedef struct alarm_tag
{
struct alarm_tag* link;
int seconds;
time_t time;
char message[64];
} alarm_t;

pthread_mutex_t alarm_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t alarm_cond = PTHREAD_COND_INITIALIZER;
alarm_t* alarm_list = NULL;
time_t current_alarm = 0;

/**
* alarm_mutex need to be locked
*/
void alarm_insert(alarm_t* alarm)
{
int status;

alarm_t* next;
alarm_t** last;
last = &alarm_list;
next = *last;

while(next != NULL)
{
if(next->time >= alarm->time)
{
alarm->link = next;
*last = alarm;
break;
}

last = &next->link;
next = next->link;
}

if(next == NULL){
*last = alarm;
alarm->link = NULL;
}

/*for test: output the list*/
printf("[list: ");
for(next = alarm_list; next != NULL; next = next->link)
{
printf("%d(%d)[/"%s/"] ", next->time, next->time-time(NULL), next->message);
}
printf("]/n");

if(current_alarm ==0 || alarm->time < current_alarm)
{
current_alarm = alarm->time;
status = pthread_cond_signal(&alarm_cond);
if(status != 0)
ERROR_ABORT(status,"Signal cond");
}

}

void* alarm_thread(void* arg)
{
alarm_t* alarm;
int sleep_time;
time_t now;
int status, expired;
struct timespec cond_time;

while(1)
{
status = pthread_mutex_lock(&alarm_mutex);
if(status != 0)
ERROR_ABORT(status, "lock");

current_alarm = 0;

while(alarm_list == NULL)
{
status = pthread_cond_wait(&alarm_cond, &alarm_mutex);
if(status != 0 )
ERROR_ABORT(status, "Wait cond");
}

alarm = alarm_list;
alarm_list = alarm->link;
now = time(NULL);
expired = 0;

if(alarm->time > now)
{
printf("[wating: %d(%d)/"%s/"]/n", alarm->time, alarm->time - time(NULL), alarm->message);

cond_time.tv_sec = alarm->time;
cond_time.tv_nsec = 0;
current_alarm = alarm->time;
while(current_alarm == alarm->time)
{
status = pthread_cond_timedwait(&alarm_cond, &alarm_mutex,&cond_time);
if(status == ETIMEDOUT)
{
expired = 1;
break;
}
}

if(!expired)
alarm_insert(alarm);
}else
expired = 1;

if(expired)
{
printf("(%d) %s/n", alarm->seconds, alarm->message);
free(alarm);
}

status = pthread_mutex_unlock(&alarm_mutex);
if(status != 0)
ERROR_ABORT(status, "Unlock mutex");
}

return 0;
}

int main()
{
pthread_t pid;
int status;
char line[128];

status = pthread_create(&pid, NULL, alarm_thread, NULL);
if(status != 0)
ERROR_ABORT(status, "pthread_create");

while(1)
{
fprintf(stdout, "Alarm>");
fgets(line, sizeof(line), stdin);
if(strlen(line) <= 0)
continue;

alarm_t* alarm = (alarm_t*)malloc(sizeof(alarm_t));
if(alarm == NULL)
ERROR_ABORT(errno,"memory can't allocated!");

if(sscanf(line, "%d %s", &alarm->seconds, alarm->message) != 2)
{
printf("Bad Command/n");
free(alarm);
continue;
}

status = pthread_mutex_lock(&alarm_mutex);
if(status != 0)
ERROR_ABORT(status, "pthread mutex locking..");

alarm->time = time(NULL) + alarm->seconds;

/* insert into list*/

alarm_insert(alarm);

status = pthread_mutex_unlock(&alarm_mutex);
if(status != 0)
ERROR_ABORT(status, "pthread mutex unlocking...");
}

return 0;
}

深入Phtread(二):线程的同步-Mutex

发表于 2009-02-11   |   分类于 Linux开发

并行的世界,没有同步,就失去了秩序,就会乱作一团!试想,交通没有红绿灯,生产线产品装配没有一定的顺序… 结果是显而易见的。多个线程也需要同步,否则程序运行起来结果不可预测,这是我们最不能容忍的。交通的同步机制就是红绿灯,Pthread提供了互斥量(mutex)和条件变量(Condition Variables)两种机制去同步线程。

  1. 不变量,临界区和判定条件
  2. 互斥量(Mutex)
  3. 创建和销毁互斥量
  4. 锁定和解锁
  5. 调整mutex大小
  6. 使用多个mutex
  7. 锁定链

1. 不变量,临界区和判定条件

不变量(Invariant):程序所做的一些假设,特别是指变量之间的关系。如:一个queue,有头节点,和其它数据节点,这些元素之间的连接关系就是不变量。当程序里面不变量遭受破坏时,后果往往是很严重的,轻则数据出错,重则程序直接崩溃。

临界区(Critical Section):处理共享数据的一段代码。

判定条件(Predicates):描述不变量状态的逻辑表达式。

2. 互斥量(Mutex)

一般,多个线程之间都会共享一些数据,当多个线程同时访问操作这些共享数据时。问题出来了,一个线程正在修改数据时,另外一个可能也去操作这些数据,结果就会变得不一致了。如(gv=0是共享的数据):

线程A:a = gv; gv = a + 10; 
线程B: b = gv; gv = a + 100;

可能发生A执行完a=gv(0)时,B开始执行b=gv(0); gv=a+100,此时gv=100,然后a执行gv=a+10,最后gv=10。并不是我们要的结果,我们的想法是两个线程并发的给gv加上一个值,期望结果110。^_^ 若这是你银行卡的余额,若没有同步,那就惨了(你往卡里打钱,你有个朋友也同时往你卡里汇钱,很有可能余额只仅加上一方打的)。

互斥量就是为了解决这种问题而设计的,它是Dijkstra信号量的一种特殊形式。它使得线程可以互斥地访问共享数据。如:

互斥量

上图展示了三个线程共享一个互斥量,位于矩形中心线下方的线程锁定了该互斥量;位于中心线上方且在矩形范围内的线程等待该互斥量被解锁,出于阻塞状态,在矩形外面的线程正常运行。刚开始,mutex是解锁的,线程1成功将其锁定,据为己有,因为并没有其它线程拥有它。然后,线程2尝试去锁定,发现被线程1占用,所以阻塞于此,等到线程1解锁了该mutex,线程2立马将mutex锁定。过了会,线程3尝试去锁定mutex,由于mutex被锁定,所以阻塞于此。线程1调用pthread_mutex_trylock尝试去锁定个mutex,发现该mutex被锁定,自己返回继续执行,并没有阻塞。继续线程2解锁,线程3锁定成功,最后线程3完成任务解锁mutex。

3. 创建和销毁互斥量

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_init(pthread_mutex_t* mutex, pthread_mutexattr_t* attr);
int pthread_mutex_destroy(pthread_mutex_t* mutex);

不要尝试去使用复制的的mutex,结果未定义。

静态创建,当mutex以extern或者static存储时,可以用PTHREAD_MUTEX_INITIALIZER初始化,此时该mutex使用默认属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#include "error.h"
#include <pthread.h>

typedef struct my_struct_tag
{
pthread_mutex_t mutex;
int value;
} my_struct_t;

my_struct_t data = { PTHREAD_MUTEX_INITIALIZER, 0};

int main()
{
return 0;
}

动态创建,往往使用mutex时,都会将它和共享数据绑在一起,此时就需要pthread_mutex_init去动态初始化了,记得用完后pthread_mutex_destroy。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

#include "error.h"
#include <pthread.h>

typedef struct my_struct_tag
{
pthread_mutex_t mutex;
int value;
} my_struct_t;

int main()
{
my_struct_t* data;
int status;

data = (my_struct_t*)malloc(sizeof(my_struct_t));
status = pthread_mutex_init(&data->mutex, NULL);
if(status != 0)
ERROR_ABORT(status, "pthread_mutex_init");

pthread_mutex_destroy(&data->mutex);
free(data);

return 0;
}

4. 锁定和解锁

原则见上面。

int pthread_mutex_lock(pthread_mutex_t* mutex);
int pthread_mutex_trylock(pthread_mutex_t* mutex);
int pthread_mutex_unlock(pthread_mutex_t* mutex);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

#include <pthread.h>
#include <sys/types.h>
#include "error.h"
#include <errno.h>

#define SPIN 10000000

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
long counter;
time_t end_time;

void* counter_thread(void* arg)
{
int status;
int spin;

while(time(NULL) < end_time)
{
status = pthread_mutex_lock(&mutex);
if(status != 0)
ERROR_ABORT(status, "Lock mutex");

for(spin = 0; spin < SPIN; spin++)
counter++;

status = pthread_mutex_unlock(&mutex);
if(status != 0)
ERROR_ABORT(status, "Unlock mutex");
sleep(1);
}

printf("Coutner is %#lx/n", counter);

return NULL;
}

void* monitor_thread(void* arg)
{
int status;
int misses = 0;

while(time(NULL) < end_time)
{
sleep(3);

status = pthread_mutex_trylock(&mutex);
if(status != EBUSY)
{
if(status != 0)
ERROR_ABORT(status, "Trylock mutex");

printf("Counter is %ld/n", counter/SPIN);
status = pthread_mutex_unlock(&mutex);
if(status != 0)
ERROR_ABORT(status, "Unlock mutex");
}else
misses++;
}
printf("Monitro thread missed update %d times./n", misses);
return NULL;
}

int main()
{
int status;
pthread_t pid_counter;
pthread_t pid_monitor;

end_time = time(NULL) + 60;

status = pthread_create(&pid_counter, NULL, counter_thread, NULL);
if(status != 0)
ERROR_ABORT(status, "fail to create thread counter");

status = pthread_create(&pid_monitor, NULL, monitor_thread, NULL);
if(status != 0)
ERROR_ABORT(status, "fail to create monitor thread");

status = pthread_join(pid_counter, NULL);
if(status != 0 )
ERROR_ABORT(status, "fail to join counter thread");

status = pthread_join(pid_monitor, NULL);
if(status != 0)
ERROR_ABORT(status, "fail to join monitor thread");

return 0;
}

5. 调整mutex大小

mutex应该多大?这里的大小是相对的,如mutex锁定到解锁之间的代码只有一行,比起有10行的就小了。 原则是:尽可能大,但不要太大(As big as neccessary, but no bigger)。考虑下面的因素:

1> mutex并不是免费的,是有开销的,不要太小了,太小了程序只忙于锁定和解锁了。

2> mutex锁定的区域是线性执行的,若太大了,没有发挥出并发的优越性。

3> 自己掂量1和2,根据实际情况定,或者尝试着去做。

6. 使用多个mutex

使用多个mutex一定要注意,防止死锁(deadlock)发生。下面是一个典型死锁:

线程A:pthread_mutex_lock(&mutex_a); pthread_mutex_lock(&mutex_b); ...
线程B:pthread_mutex_lock(&mutex_b); pthread_mutex_lock(&mutex_a); ...

存在这种可能,线程A执行了第一句,锁定了mutex_a;然后线程开始执行第一句锁定mutex_b;然后他们互相等待解锁mutex,A等mutex_b被解锁,B等mutex_a被解锁,不肯让步,出于死锁状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

#include <pthread.h>
#include "error.h"
#include <time.h>

pthread_mutex_t mutex_a = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex_b = PTHREAD_MUTEX_INITIALIZER;

void* thread1(void* arg)
{
while(1)
{
/*sleep(1);*/
pthread_mutex_lock(&mutex_a);
pthread_mutex_lock(&mutex_b);

printf("[%lu]thread 1 is running! /n", time(NULL));

pthread_mutex_unlock(&mutex_b);
pthread_mutex_unlock(&mutex_a);
}
return NULL;
}

void* thread2(void* arg)
{
while(1)
{
/*sleep(1);*/

pthread_mutex_lock(&mutex_b);
pthread_mutex_lock(&mutex_a);

printf("[%lu]thread 2 is running! /n",time(NULL));

pthread_mutex_unlock(&mutex_a);
pthread_mutex_unlock(&mutex_b);

}
return NULL;
}

int main()
{
pthread_t tid1, tid2;
int status;

status = pthread_create(&tid1, NULL, thread1, NULL);
if(status != 0)
ERROR_ABORT(status, "thread 1");

status = pthread_create(&tid2, NULL, thread2, NULL);
if(status !=0)
ERROR_ABORT(status, "thread 2");

status = pthread_join(tid1, NULL);
if(status != 0)
ERROR_ABORT(status, "join thread1");

status = pthread_join(tid2, NULL);
if(status != 0)
ERROR_ABORT(status, "join thread2");
}

解决死锁的方法:

a. 固定锁定顺序(Fixed locking hierarchy):锁定mutex的顺序固定。

线程A:pthread_mutex_lock(&mutex_a); pthread_mutex_lock(&mutex_b); ...
线程B:pthread_mutex_lock(&mutex_a); pthread_mutex_lock(&mutex_b); ...

b. 尝试和回退(Try and back off): 锁定第一个后,尝试锁定下一个,若锁定成功,继续尝试下一个,若锁定失败,解锁先去锁定的。

解锁顺序不会引起死锁.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

#include <pthread.h>
#include "error.h"
#include <errno.h>

#define ITERATIONS 100


pthread_mutex_t mutex[3] = {
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER,
PTHREAD_MUTEX_INITIALIZER
};

int backoff = 1;
int yield_flag = 0;

void* lock_forward(void* arg)
{
int i, iterate, backoffs;
int status;

for(iterate = 0; iterate < ITERATIONS; iterate++)
{
backoffs = 0;
for(i = 0; i < 3; i++){
if(i == 0)
{
status = pthread_mutex_lock(&mutex[i]);
if(status != 0)
ERROR_ABORT(status,"Lock mutex");
}else
{
if(backoff)
status = pthread_mutex_trylock(&mutex[i]);
else
status = pthread_mutex_lock(&mutex[i]);

if(status == EBUSY)
{
backoff++;
printf("forward locker backing off at %d./n", i);
for(; i >= 0; i--)
{
status = pthread_mutex_unlock(&mutex[i]);
if(status != 0)
ERROR_ABORT(status, "Unlock mutex");
}
}else
{
if(status != 0)
ERROR_ABORT(status, "Lock mutex");

printf("forward locker got %d /n", i);
}
}

if(yield_flag){
if(yield_flag > 0)
sched_yield();
else
sleep(1);
}
}

printf("lock forward got all locks , %d backoffs/n", backoffs);

pthread_mutex_unlock(&mutex[2]);
pthread_mutex_unlock(&mutex[1]);
pthread_mutex_unlock(&mutex[0]);
sched_yield();
}

return NULL;
}

void* lock_backward(void* arg)
{
int i, iterate, backoffs;
int status;

for(iterate = 0; iterate < ITERATIONS; iterate++)
{
backoffs = 0;
for(i = 2; i >= 0; i--){
if(i == 2)
{
status = pthread_mutex_lock(&mutex[i]);
if(status != 0)
ERROR_ABORT(status,"Lock mutex");
}else
{
if(backoff)
status = pthread_mutex_trylock(&mutex[i]);
else
status = pthread_mutex_lock(&mutex[i]);

if(status == EBUSY)
{
backoff++;
printf("backward locker backing off at %d./n", i);
for(; i < 3; i++)
{
status = pthread_mutex_unlock(&mutex[i]);
if(status != 0)
ERROR_ABORT(status, "Unlock mutex");
}
}else
{
if(status != 0)
ERROR_ABORT(status, "Lock mutex");

printf("backward locker got %d /n", i);
}
}

if(yield_flag){
if(yield_flag > 0)
sched_yield();
else
sleep(1);
}
}

printf("lock backward got all locks , %d backoffs/n", backoffs);

pthread_mutex_unlock(&mutex[0]);
pthread_mutex_unlock(&mutex[1]);
pthread_mutex_unlock(&mutex[2]);
sched_yield();
}


return NULL;
}

int main(int argc, char* argv[])
{
pthread_t forward, backward;
int status;

if(argc > 1)
backoff = atoi(argv[1]);

if(argc > 2)
yield_flag = atoi(argv[2]);

status = pthread_create(&forward, NULL, lock_forward, NULL);
if(status != 0)
ERROR_ABORT(status, "Create forward");

status = pthread_create(&backward, NULL, lock_backward, NULL);
if(status != 0)
ERROR_ABORT(status, "Create backward");

pthread_exit(NULL);
}

7. 锁定链

一般用于遍历数据结果(树,链表),一个用于锁定指针,一个锁定数据。

形如:

pthread_mutex_lock(&mutex_a); 
pthread_mutex_lock(&mutex_b); 
...
pthread_mutex_unlock(&mutex_a)
...
pthread_mutex_unlock(&mutex_b)

注意,锁定链往往会出现大量的锁定和解锁操作,有时会得不偿失。

深入Phtread(一):线程的一生

发表于 2009-02-10   |   分类于 Linux开发

我们每个人都并行地活在这个世界上 ,每一天每个人都干着不同的事情。每个人的人生都是不同的,从出生 -> 活着 -> 死去, 个中滋味,只能自己体味了。我们的线程兄弟也一样,只不过它的环境没有人类社会这么复杂,它的一生,被操作系统控制,被我们程序员控制着!呵呵,想想都觉得这兄弟可怜啊!不过这哥们可不许小瞧了,功能大了去了!具体线程的定义和好处参考其它关于线程的资料。该篇主要讲线程兄弟的大体的一生(从被创建到销毁)。进入正题:

线程的一生始终处于下面四种状态之一:

  • Ready 就绪状态,等待处理器的调度。可能是刚新创建的,或阻塞状态,等待的资源得到满足刚解除阻塞状态,或处于运行状态被别的线程抢占了处理器。

  • Running 运行状态,线程正在处理器上运行。多处理器可能会不止一个线程正在运行。

  • Blocked 阻塞状态,线程等待某些资源,不能运行。如:等待一个条件变量,锁定互斥量,或者等待I/O操作的完成。

  • Terminated 终止状态,线程从它的开始函数(创建时指定的)终止。调用pthread_exit或者被其它线程取消(cancelled)。此时,线程并没有被分离(detached),也没有被连接(joined)。一旦线程被joined或detached, 将会被系统回收。

线程的状态图:

线程状态图

1. 创建和使用线程常见的pthread函数

1
2
3
4
5
6
7
8
9

pthread_t thread;
int pthread_equal(pthread_t t1, pthread_t t2);
int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*start)(void*), void* arg);
pthread_t pthread_self();
int sched_yield();
int pthread_exit(void* value_ptr);
int pthread_detach(pthread_t thread);
int pthread_join(pthread_t thread, void** value_ptr);

2. 创建线程:

有一个比较特殊的线程, 就是主线程或者称为初始线程,它是当一个进程被创建时创建的。而其他线程则是在初始线程中调用pthread_create创建的。创建的新线程初始为Ready状态,等待处理器调度。

3. 执行线程:

线程被创建后,就会执行phtread_create的start参数指定的函数,我们可以通过pthread_create的arg参数向线程开始函数传递一个参数,若不想传就直接赋值NULL。

上面提到了一个特殊的线程:初始线程,它的开始函数就是我们学习C/C++见到的第一个函数main,只不过这个开始函数不是由我们来调用的,而是由操作系统Shell。操作系统先初始化进程,然后运行主线程的开始函数main。注意:初始线程和我们自己创建的线程有稍许不同:-开始函数参数不同,main的参数是int arg, char argv,而我们自己的线程参数是void arg。-自己创建的线程开始函数返回了,其它线程可以继续执行。而初始线程开始函数main返回后进程会终止,导致其它正在运行的线程也就被强行终止,若不想在main退出后关闭进程,可以在main最后调用pthread_exit,不过此时该进程就成了僵尸进程(defunct),直到所有线程执行完毕。- 还有一个很重要的区别,多数系统上,初始线程使用的是进程的栈,而自己创建的线程则使用自己的栈,往往自建线程的栈没有初始线程的栈空间大,要注意线程栈溢出哦!(phread提供了修改线程栈大小的函数,后面再说^_^)

4. 运行和阻塞

线程兄弟和我们一样,不能一直都醒着工作,它也要休息。线程一生大多处于三种状态:ready, running, 和blocked(和我一样,吃饭,工作,睡觉^_^)。 线程被创建时出于就绪状态,就绪状态等待处理器,处理器有空闲了,线程就转到运行状态疯狂工作,突然发现自己需要的其它资源(互斥量,条件变量)在别的线程手里,或者别的线程优先级比自己大一下从它手里抢走了处理器,或处理器烦了它了(时间片已经用完),或自己不好意思不干事还占用处理器了(等待I/O操作完成),就转换成阻塞状态,停止运行,可以休息下了。 休息可不是给它放长假,任务还没完成了…继续,当等待的资源得到满足,再次投入工作,进入运行状态。周而复始,直到任务完成。

5. 终止线程:

线程一般都是在开始函数执行完时终止的。线程开始函数中调用pthread_exit或者其它线程调用pthread_cancel都可以终止线程。终止后线程处于terminated状态(注意:不是destroyed),然后等待系统回收。

若创建线程时指定线程是detached的,直接在线程开始函数执行完后就会被回收。

若是joinable,那就需要初始线程或其他线程调用pthread_join来等待要终止的线程了, 同时还可以通过pthread_joind的第二个参数获得线程的返回值。pthread_join后,线程就被detached,然后被系统回收。

6. 回收线程

线程创建时,detachsate属性是PTHREAD_CREATE_DETACHED,则在开始函数返回后被回收。

或其他线程使用了pthread_join 或自己调用了pthread_detach,线程出于terminated状态后,立马就会被系统回收:释放系统资源和进程资源,包含线程返回值占用的内存,线程堆栈,寄存器状态等等。

今天就到此,以后深挖!^_^

1…91011…13
David++

David++

123 日志
25 分类
65 标签
RSS
GitHub 知乎 微博 豆瓣
© 2007 - 2021 David++
由 Hexo 强力驱动
主题 - NexT.Pisces