Profil de Libin绿色家园PhotosBlogListes Outils Aide

Blog


25 janvier

Brief list of CvsIn features

 

Installation

Easy installation:
1. Run DevStudio.
2. Go to menu: Tools->Customize.
3. Select Add-ins and Macro Files tab.
4. Use Browse button to locate and select CvsIn.dll file.
5. Make sure the checkbox to the left of CvsIn is checked.
6. Click Close button.

Note:
To uninstall simply un-check the checkbox next to the CvsIn and press Close button.
You can also close DevStudio and remove CvsIn.dll file.

User interface consist of the following:
1. Toolbars.
    o CvsIn Options - to invoke the Options dialog where you can change the CvsIn settings.
    o Run WinCvs - launch WinCvs for the current file, or find already running one.
    CVS commands toolbars:
   
o CVS update - cvs update <active file>
    o CVS commit - cvs commit <active file>
    o Query CVS update - cvs -n update <active file>
    o CVS diff - cvs diff <active file>
    o CVS log - cvs log <active file>
    o CVS status - cvs status <active file>
    o CVS edit - cvs edit <active file>
    o CVS unedit - cvs unedit <active file>
    o CVS watch - cvs watch add <active file>
    o CVS release watch - cvs watch remove <active file>
    o CVS tag - cvs tag <active file>
    o CVS untag - cvs tag -d <active file>
    o CVS fork - cvs tag -b <active file>
    o CVS unlock - cvs admin -u <active file>
    Addtional toolbars:
   
o Graph - show the graph or revisions and tags (Not yet implemented - will lauch WinCvs!)
    o Wizard - not frequently used CVS commands (add/delete etc.) and special tools (Not yet
    implemented!
)
2. Macro sheet in the Output Window. You will see all the messages from CvsIn here.
3. Status Bar - short information about the activated file will be displayed here. Currently it Displays the information about file attributes in the following way:
    o [rahs] - Read-only, Archive, Hidden, System.
    o if the attribute is not set then the '-' is displayed.
    o Example: [ra--] - Read-only, Archiv file.

Note:
You can use more commands and assign keybindings for easier access for all the functions.

Note
:
To use the Status Bar you don't need to have
Visual Assist and AIC installed any more, but it is still using VA in the first attempt and it is recommended configuration.

The CVS command related setup is similiar to the one you have in WinCvs, but it more basic.
1. HOME folder - setup the same HOME as in WinCvs - CVS will for the password in this directory. If you have set HOME environmental variable it will be used instead.
2. CVS client  command line - it is best to have the cvs client in the PATH. If you don't want it that way you should setup the full path here.
3. Checkout Read-only - if selected then all new working files will have the Read-only attribute set. CVS global option: [-r]
4. Messages - you can set the amount of messages you get from CVS client. [-q][-Q]
5. TCP/IP compression - you can setup the level of compression during CVS oprerations
6. Other options - you can add any other options - it will be used as a global options for invoking CVS client commands.
7. External diff program - you can specify the external diff program to compare the file with the version on the server. Not yet implemented! (but you can setup path already)

Few things to remmember when using CVS commands:
1. After you get he file and unzip it - you have to go to the
   DevStudio Tools->Customize and uninstall previous version of CvsIn (uncheck
   the box then click the Close).
2. Then install it again - this way you will get all the new buttons
   on the screen.
3. After that - go to the CvsIn Options and make sure you have the
   settings of HOME and cvs client set correctly.
4. You have to make sure CvsIn can see the cvs client - either add
   the location of the cvs.exe to your PATH, or specify the full path
   in the settings.
5. Remember that DevStudio doesn't scrool the output window - you
   will have to go to the "Macro" sheet of the Output window and
   place the cursor at the end - click there and then use "Ctrl+End".
6. Do NOT exit the DevStudio if the CVS command is running. If that
   happens - you will have to use the task manager and kill the
   process by hand. Look after "cvs".
7. If you use the DialUp connection - it is better to select the
   DialUp in the CvsIn Options->CvsIn or the DialUp dialog won't
   popup - that is M$ bug...
8. At the moment all the buttons are working excpet the Graph(calls
   WinCvs) and Wizard(not finished yet). I expose the buttons already
   because what you get now it the final, fixed set of buttons - for
   the next upgrade you will not have to uninstall CvsIn any more
   since the buttons won't change. All the other buttons willgo the
   the wizard dialog in the future.

WinCvs options

From the CvsIn Options settings dialog you can control how launching of WinCvs is handled:
1. WinCvs path - you need to tell CvsIn where is your WinCvs installed. By default this is set to "C:\Program Files\GNU\WinCvs 1.1\wincvs.exe" where WinCvs is usually installed. You should have WinCvs 1.1b12 (or latest) version isntalled. If you installed WinCvs to the different directory you can use the browse button next to the EditBox to locate it.
2. Start in - points to the directory where WinCvs is installed. When you select the file for WinCvs path the directory will be filled in automatically.
3. Run - You can control the way WinCvs is launched, the options are:
    o Normal Window - the default setting,
    o Minimized,
    o Maximized.
4. Close all on DevStudio exit - CvsIn maintains open WinCvs instances and if this checkbox is selected then it will close all running WinCvs instances under it's control when you exit DevStudio.
5. Save all documents before running WinCvs - if selected CvsIn will save all unsaved documents before run WinCvs.
6. Reuse WinCvs if still active for the same file - if selected then to avoid multipling copies of running WinCVs CvsIn will check if the instance of WinCvs is already running for that file and if it finds such instance it will just bring it to foreground.
7. Attempt to reuse WinCvs for file groups - similiar to previous option (6). CvsIn will attempt to identify the groups of files (like .cpp and corresponding header).
8. Signature - helps to identify the main window of running WinCvs. The users of some old versions of WinCvs will notice that bringing to foreground option does not keep the focus right. It is because the older version of WinCvs have no signature text in the title. Solution: Upgrade! As an alternative you can try to use " - " as a signature - select from the ComboBox.
9. Extension delimiter (period) - if option for file groups (7) is selected thee delimiter is searched from the left or right side - depending on the selection. It allows to create the files groups depending on the extension of the file or on the name of it.

CvsIn options

From the CvsIn Options settings dialog you can change some CvsIn specific settings:

1. Output group of settings - controls CvsIn's messages:
    o Use Output Window - you can turn on/off the messages in the Macro sheet in the Output Window.
    o Use Status Bar - you can turn on/off the messages send to the DevStudio Status Bar. This option works best if Visual Assist and AIC are installed!
    o Use Log File - you can turn on/off logging messages to the Log File. Not yet implemented!

2. Warnings/Prompts group of settings - controls warious warnings:
    o Soft Warning - to avoid annoying message boxes etc. you can utylize the VA's let margin and status bar for warnings. Require VA, not yet implemented!
    o Activate file in ../Cvs/Base/ - this warning prevents you from editing the copy of the files copied to the CVS administrative folder when use "cvs edit".
    o Resource files Read-only - warning if the "<proj_name>.rc" or "resource.h" is Read-0only. Not yet implemented!

3. Tools group of settings - you can find some useful tools:
   o View Log File - see the logged messages in the Log File. Not yet implemented!
   o Clear Log File - remove the contents of the log file. Not yet implemented!

4. Misc group of settings - miscellaneous options:
   o Dial-Up connection - set this if you are connected using Dial-Up to make sure you will see the "Connect" dialog. If you're over the LAN then uncheck.
   o Save before CVS command - if set then all files will be saved before invoking CVS command.

CvsIn options

You can now easier manipulate your WinCvs sessions. By pressing the control keys while pushing the 'RunWinCvs' button you'll get various behaviour:
1. Ctr+'RunWinCvs' - this will invoke the sessions manager, which will allow you to:
    o Refresh the list of running sessions
    o Switch to selected seesions
    o Open new session for the selected file - previous session is closed!
    o Close the selected session
    o Close all sessions
Note: The list control is hot - one click activate!
2. Ctr+Shift+'RunWinCvs' - the list of active sessions will be printed to the Output Window
3. Shift+RunWinCvs - open the new WinCvs session without the attached file, regardless of the active file exists or not. It is useful to emulate the 'single instance of Browser (WinCvs)' working model model.

16 janvier

Excel技巧


  "End+方向键"进行快速定位至最边上单元格

  如图1所示,假如当前单元格在c3的位置,按End键后再按↑键,当前单元格则会跳到最上方c1单元格,而如果按的是↓键则会跳到最下文的c5单元格,按←键会跳到最左方单元格,按→键会跳到最右方单元格。也就是说不管当前单元格在哪个位置,反正按End键后再按方向键就会定位到最边上的单元格。而如果中间有空白单元格则会先会定位至空白单元格。这个作用是如果工作表中有很多行或列时,用此快捷键则可以快速找到空白单元格或定位至边上单元格。



  隔行列快速粘贴

  如图2所示,按Ctrl键后同时选中a1、e1、a5、e5四个单元格,复制后粘贴会发现复制的内容连接到了一起。这样可以达到快速复制不连接单元格的作用。当然如果中间有空白单元格则是无法选中的。



  "填充内容"#与"填充内容"@单元格格式类型

  当你需要输入大量前面部分或后面部分的相同内容时,可以在单元格格式中设置单元格类型为"填充内容"#或#"填充内容",其中""填充内容""为自动填充的相同部分的内容,"#"为手动输入的部分。输入数字时用"#",输入字符时用"@"。

   中英文输入自动切换

  在编辑表格时,有的单元格中要输入英文,有的单元格中要输入中文,反复切换输入法实在不方便,这时可以选中需要输入中文的单元格区域,执行"数据→有效性"命令,打开"数据有效性"对话框,切换到"输入法模式"标签下,按"模式"右侧的下拉按钮,选中"打开"选项后确定退出。 现在当选中需要输入中文的单元格时,中文输入法就会自动打开,而当选中其它单元格时,中文输入法自动关闭。

  用连字符"&"来合并文本

  如果想将多列的内容合并到一列中,不需要利用函数,用连字符"&"就能够轻松将它搞定。在最后合并的单元格中输入公式:="单元格1"&"单元格2",回车后即可合并。

  按"Ctrl+Enter"组合键后定位在同一单元格

  有时,为了测试某个公式,需要在某个单元格内反复输入多个测试值。但是每次输入一个值后按下Enter键查看结果后,活动单元格就会默认移到下一个单元格上,必须用鼠标或上移箭头重新选定原单元格,极不方便。如果你按"Ctrl+Enter"组合键,则活动单元格仍为当前单元格。

  自动打开工作簿

  将需要自动打开的工作薄的快捷方式放到C:\Program Files\Microsoft Office\Office10\XLStart文件夹中,以后每次启动时,Excel都会自动打开这个工作簿。

  在选定单元格中输入内容

  如果在一行中输入完后想切换至下一行,这时一般用鼠标移动并定位至要输入的单元格再开始输入,这样感觉很麻烦。不过我们可以先选中要输入的区域,然后可以按Tab键进行切换,如果要退回原来的单元格则可以用Shift+Tab键。

  快速打印多个工作表

  打印多个工作表时无需一个一个打印,可以直接选中需要打印的工作表的标签,然后点击工具栏的打印按钮就可以了。

  用选择性粘贴的"转置"功能快速转换行列

  有时我们需要把Word中的一个表格的行列进行转换,这时我们可以复制后,在Excel中执行"选择性粘贴→转置"来快速转换,准确又方便。
14 janvier

pthread多线程编程整理(三)

一.pthread_create()之前的属性设置
1.线程属性设置
我们用pthread_create函数创建一个线程,在这个线程中,我们使用默认参数,即将该函数的第二个参数设为NULL。的确,对大多数程序来说,使用默认属性就够了,但我们还是有必要来了解一下线程的有关属性。
属性结构为pthread_attr_t,它同样在头文件pthread.h中定义,属性值不能直接设置,须使用相关函数进行操作,初始化的函数为 pthread_attr_init,这个函数必须在pthread_create函数之前调用。属性对象主要包括是否绑定、是否分离、
堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省的堆栈、与父进程同样级别的优先级。

2.绑定
关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制一个或多个线程。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。
  设置线程绑定状态的函数为 pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值: PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。下面的代码即创建了一个绑定的线程。
#i nclude <pthread.h>
pthread_attr_t attr;
pthread_t tid;
/*初始化属性值,均设为默认值*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_create(&tid, &attr, (void *) my_function, NULL);

3.线程分离状态                                                                                                                                         线程的分离状态决定一个线程以什么样的方式来终止自己。非分离的线程终止时,其线程ID和退出状态将保留,直到另外一个线程调用pthread_join.分离的线程在当它终止时,所有的资源将释放,我们不能等待它终止。                                                                                         设置线程分离状态的函数为 pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是在多线程编程里常用的方法。
4.优先级                                                                                                                                                 它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数 pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。下面即是一段简单的例子。

#i nclude <pthread.h>
#i nclude <sched.h>
pthread_attr_t attr; pthread_t tid;
sched_param param;
int newprio=20;
/*初始化属性*/
pthread_attr_init(&attr);
/*设置优先级*/
pthread_attr_getschedparam(&attr, &param);  
param.sched_priority=newprio;
pthread_attr_setschedparam(&attr, &param);
pthread_create(&tid, &attr, (void *)myfunction, myarg);

二.线程数据处理                                                                                                                                 和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据段,可以方便的获得、修改数据。但这也给多线程编程带来了许多问题。我们必须当心有多个不同的进程访问相同的变量。许多函数是不可重入的,即同时不能运行一个函数的多个拷贝(除非使用不同的数据段)。在函数中声明的静态变量常常带来问题,函数的返回值也会有问题。因为如果返回的是函数内部静态声明的空间的地址,则在一个线程调用该函数得到地址后使用该地址指向的数据时,别的线程可能调用此函数并修改了这一段数据。在进程中共享的变量必须用关键字volatile来定义,这是为了防止编译器在优化时(如gcc中使用-OX参数)改变它们的使用方式。为了保护变量,我们必须使用信号量、互斥等方法来保证我们对变量的正确使用。
1.线程数据                                                                                                                                                 在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific Data)。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。例如我们常见的变量errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在 A线程里输出的很可能是B线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。
  和线程数据相关的函数主要有4个:创建一个键;为一个键指定线程数据;从一个键读取线程数据;删除键。
  创建键的函数原型为:
  int pthread_key_create __P ((pthread_key_t *__key,void (*__destr_function) (void *)));                                                                                                                                                 第一个参数为指向一个键值的指针,第二个参数指明了一个destructor函数,如果这个参数不为空,那么当每个线程结束时,系统将调用这个函数来释放绑定在这个键上的内存块。这个函数常和函数pthread_once ((pthread_once_t*once_control, void (*initroutine) (void)))一起使用,为了让这个键只被创建一次。函数pthread_once声明一个初始化函数,第一次调用pthread_once时它执行这个函数,以后的调用将被它忽略。
int pthread_key_delete(pthread_key_t *key);
该函数用于删除一个由pthread_key_create 函数调用创建的键。调用成功返回值为0,否则返回错误代码。
在下面的例子中,我们创建一个键,并将它和某个数据相关联。我们要定义一个函数 createWindow,这个函数定义一个图形窗口(数据类型为Fl_Window *,这是图形界面开发工具FLTK中的数据类型)。由于各个线程都会调用这个函数,所以我们使用线程数据。
/* 声明一个键*/
pthread_key_t myWinKey;
/* 函数 createWindow */
void createWindow ( void ) {
Fl_Window * win;
static pthread_once_t once= PTHREAD_ONCE_INIT;
/* 调用函数createMyKey,创建键*/
pthread_once ( & once, createMyKey) ;
/*win指向一个新建立的窗口*/
win=new Fl_Window( 0, 0, 100, 100, "MyWindow");
/* 对此窗口作一些可能的设置工作,如大小、位置、名称等*/
setWindow(win);
/* 将窗口指针值绑定在键myWinKey上*/
pthread_setpecific ( myWinKey, win);
}
/* 函数 createMyKey,创建一个键,并指定了destructor */
void createMyKey ( void ) {
pthread_keycreate(&myWinKey, freeWinKey);
}
/* 函数 freeWinKey,释放空间*/
void freeWinKey ( Fl_Window * win){
delete win;
}
这样,在不同的线程中调用函数createMyWin,都可以得到在线程内部均可见的窗口变量,这个变量通过函数 pthread_getspecific得到。在上面的例子中,我们已经使用了函数pthread_setspecific来将线程数据和一个键绑定在一起。这两个函数的原型如下:
  
int pthread_setspecific __P ((pthread_key_t __key,__const void *__pointer)); 该函数设置一个线程专有数据的值,赋给由pthread_key_create 创建的键,调用成功返回值为0,否则返回错误代码。
void *pthread_getspecific __P ((pthread_key_t __key));                                                  该函数获得绑定到指定键上的值。调用成功,返回给定参数key 所对应的数据。如果没有数据连接到该键,则返回NULL。

  这两个函数的参数意义和使用方法是显而易见的。要注意的是,用pthread_setspecific为一个键指定新的线程数据时,必须自己释放原有的线程数据以回收空间。这个过程函数pthread_key_delete用来删除一个键,这个键占用的内存将被释放,但同样要注意的是,它只释放键占用的内存,并不释放该键关联的线程数据所占用的内存资源,而且它也不会触发函数pthread_key_create中定义的destructor函数。线程数据的释放必须在释放键之前完成。
2.互斥锁                                                                                                                                                 假设各个现成向同一个文件顺序写入数据,最后得到的结果是不可想象的。所以用互斥锁来保证一段时间内只有一个线程在执行一段代码。



使用int pthread_mutex_lock锁住互斥锁,使用int pthread_mutex_unlock解琐。
如果我们试图为一个已被其他线程锁住的互斥锁加锁,程序便会阻塞直到该互斥对象解锁。
如果在共享内存中分配一个互斥锁,我们必须在运行时调用ptgread_mutex_init函数尽心初始化。
void reader_function ( void );
void writer_function ( void );
char buffer;
int buffer_has_item=0;
pthread_mutex_t mutex;
struct timespec delay;
void main ( void ){
pthread_t reader;
/* 定义延迟时间*/
delay.tv_sec = 2;
delay.tv_nec = 0;
/* 用默认属性初始化一个互斥锁对象*/
pthread_mutex_init (&mutex,NULL);
pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);
writer_function( );
}
void writer_function (void){
while(1){
/* 锁定互斥锁*/
pthread_mutex_lock (&mutex);
if (buffer_has_item==0){
buffer=make_new_item( );
buffer_has_item=1;
}
/* 打开互斥锁*/
pthread_mutex_unlock(&mutex);
pthread_delay_np(&delay);
}
}
void reader_function(void){
while(1){
pthread_mutex_lock(&mutex);
if(buffer_has_item==1){
consume_item(buffer);
buffer_has_item=0;
}
pthread_mutex_unlock(&mutex);
pthread_delay_np(&delay);
}
}
函数 pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数 pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数 pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared,它有两个取值, PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步,后者用于同步本进程的不同线程。在上面的例子中,我们使用的是默认属性PTHREAD_PROCESS_ PRIVATE。后者用来设置互斥锁类型,可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、 PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机制,一般情况下,选用最后一个默认属性。
需要注意的是在使用互斥锁的过程中很有可能会出现死锁:两个线程试图同时占用两个资源,并按不同的次序锁定相应的互斥锁,例如两个线程都需要锁定互斥锁1和互斥锁2,a线程先锁定互斥锁1,b线程先锁定互斥锁2,这时就出现了死锁。此时我们可以使用函数 pthread_mutex_trylock,它是函数pthread_mutex_lock的非阻塞版本,当它发现死锁不可避免时,它会返回相应的信息,程序员可以针对死锁做出相应的处理。另外不同的互斥锁类型对死锁的处理不一样,但最主要的还是要程序员自己在程序设计注意这一点。
3.条件变量
互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线承间的同步。
条件变量的结构为pthread_cond_t,函数pthread_cond_init()被用来初始化一个条件变量。它的原型为:

int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));
         
其中cond是一个指向结构pthread_cond_t的指针,cond_attr是一个指向结构pthread_condattr_t的指针。结构 pthread_condattr_t是条件变量的属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用,默认值是 PTHREAD_ PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或被释放。
在pthread中,条件变量是一个pthread_cond_t类型的变量,条件变量使用下面两个函数:

pthread_cond_wait函数用于阻塞,线程可以被函数pthread_cond_signal和函数    pthread_cond_broadcast唤醒,但是要注意的是,条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如一个变量是否为0等等,这一点我们从后面的例子中可以看到。线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下一次唤醒。这个过程一般用while语句实现。
另一个用来阻塞线程的函数是pthread_cond_timedwait()它比函数pthread_cond_wait()多了一个时间参数,经历abstime段时间后,即使条件变量不满足,阻塞也被解除。
函数pthread_cond_signal()用来释放被阻塞在条件变量cond上的一个线程。
函数pthread_cond_broadcast(pthread_cond_t *cond)用来唤醒所有被阻塞在条件变量cond上的线程。这些线程被唤醒后将再次竞争相应的互斥锁,所以必须小心使用这个函数。
下面是使用函数pthread_cond_wait()和函数pthread_cond_signal()的一个简单的例子:
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;
decrement_count () {
pthread_mutex_lock (&count_lock);
while(count==0)
pthread_cond_wait( &count_nonzero, &count_lock);
count=count -1;
pthread_mutex_unlock (&count_lock);
}

increment_count(){
pthread_mutex_lock(&count_lock);
if(count==0)
pthread_cond_signal(&count_nonzero);
count=count+1;
pthread_mutex_unlock(&count_lock);
}
count值为0时, decrement函数在pthread_cond_wait处被阻塞,并打开互斥锁count_lock。此时,当调用到函数 increment_count时,pthread_cond_signal()函数改变条件变量,告知decrement_count()停止阻塞。

 

pthread多线程编程整理(二)

补充:

在传统的Unix模型中,当一个进程需要由另一个实体执行某件事时,该进程派生(fork)一个子进程,让子进程去进行处理。Unix下的大多数网络服务器程序都是这么编写的,即父进程接受连接,派生子进程,子进程处理与客户的交互。

虽然这种模型很多年来使用得很好,但是fork时有一些问题:

1. fork是昂贵的。内存映像要从父进程拷贝到子进程,所有描述字要在子进程中复制等等。目前有的Unix实现使用一种叫做写时拷贝(copy-on-write)的技术,可避免父进程数据空间向子进程的拷贝。尽管有这种优化技术,fork仍然是昂贵的。

2. fork子进程后,需要用进程间通信(IPC)在父子进程之间传递信息。Fork之前的信息容易传递,因为子进程从一开始就有父进程数据空间及所有描述字的拷贝。但是从子进程返回信息给父进程需要做更多的工作。

线程有助于解决这两个问题。线程有时被称为轻权进程(lightweight process),因为线程比进程"轻权",一般来说,创建一个线程要比创建一个进程快10~100倍。

一个进程中的所有线程共享相同的全局内存,这使得线程很容易共享信息,但是这种简易性也带来了同步问题。

一个进程中的所有线程不仅共享全局变量,而且共享:进程指令、大多数数据、打开的文件(如描述字)、信号处理程序和信号处置、当前工作目录、用户ID和组ID。但是每个线程有自己的线程ID、寄存器集合(包括程序计数器和栈指针)、栈(用于存放局部变量和返回地址)、error、信号掩码、优先级。在Linux中线程编程符合Posix.1标准,称为Pthreads。所有的pthread函数都以pthread_开头。以下先讲述5个基本线程函数, 在调用它们前均要包括pthread.h头文件。然后再给出用它们编写的一个TCP客户/服务器程序例子。

第一个函数:

 

int pthread_create (pthread_t *tid,const pthread_attr_t *attr,void *      (*func)(void *),void *arg);

一个进程中的每个线程都由一个线程ID(thread ID)标识,其数据类型是pthread_t(常常是unsigned int)。如果新的线程创建成功,其ID将通过tid指针返回。

每个线程都有很多属性:优先级、起始栈大小、是否应该是一个守护线程等等,当创建线程时,我们可通过初始化一个pthread_attr_t变量说明这些属性以覆盖缺省值。我们通常使用缺省值,在这种情况下,我们将attr参数说明为空指针。

最后,当创建一个线程时,我们要说明一个它将执行的函数。线程以调用该函数开始,然后或者显式地终止(调用pthread_exit)或者隐式地终止(让该函数返回)。函数的地址由func参数指定,该函数的调用参数是一个指针arg,如果我们需要多个调用参数,我们必须将它们打包成一个结构,然后将其地址当作唯一的参数传递给起始函数。

在func和arg的声明中,func函数取一个通用指针(void *)参数,并返回一个通用指针(void *),这就使得我们可以传递一个指针(指向任何我们想要指向的东西)给线程,由线程返回一个指针(同样指向任何我们想要指向的东西)。调用成功,返回0,出错时返回正Exxx值。Pthread函数不设置errno。

第二个函数:

 

int pthread_join(pthread_t tid,void **status);

该函数等待一个线程终止。把线程和进程相比,pthread_creat类似于fork,而pthread_join类似于waitpid。我们必须要等待线程的tid,很可惜,我们没有办法等待任意一个线程结束。如果status指针非空,线程的返回值(一个指向某个对象的指针)将存放在status指向的位置。

第三个函数:

 

pthread_t pthread_self(void);

线程都有一个ID以在给定的进程内标识自己。线程ID由pthread_creat返回,我们可以pthread_self取得自己的线程ID。

第四个函数:

 

int pthread_detach(pthread_t tid);

线程或者是可汇合的(joinable)或者是脱离的(detached)。当可汇合的线程终止时,其线程ID和退出状态将保留,直到另外一个线程调用pthread_join。脱离的线程则像守护进程:当它终止时,所有的资源都释放,我们不能等待它终止。如果一个线程需要知道另一个线程什么时候终止,最好保留第二个线程的可汇合性。 Pthread_detach函数将指定的线程变为脱离的。该函数通常被想脱离自己的线程调用,如:pthread_detach (pthread_self ( ));

 




 

第五个函数:

void pthread_exit(void *status);

该函数终止线程。如果线程未脱离,其线程ID和退出状态将一直保留到调用进程中的某个其他线程调用pthread_join函数。指针status不能指向局部于调用线程的对象,因为线程终止时这些对象也消失。有两种其他方法可使线程终止:

1. 启动线程的函数(pthread_creat的第3个参数)返回。既然该函数必须说明为返回一个void指针,该返回值便是线程的终止状态。

2. 如果进程的main函数返回或者任何线程调用了exit,进程将终止,线程将随之终止。

 

pthread多线程编程整理(一)

1 Introduction
不用介绍了吧…
2 Thread Concepts
1.     Thread由下面部分组成:
a.     Thread ID
b.     Stack
c.     Policy
d.     Signal mask
e.     Errno
f.      Thread-Specific Data
3 Thread Identification
1.     pthread_t用于表示Thread ID,具体内容根据实现的不同而不同,有可能是一个Structure,因此不能将其看作为整数
2.     pthread_equal函数用于比较两个pthread_t是否相等

#i nclude <pthread.h>
 
int pthread_equal(pthread_t tid1, pthread_t tid2)
3.     pthread_self函数用于获得本线程的thread id

#i nclude <pthread.h>
 
pthread _t pthread_self(void);
 
4 Thread Creation
1.     创建线程可以调用pthread_create函数:

#i nclude <pthread.h>
 
int pthread_create(
       pthread_t *restrict tidp,
       const pthread_attr_t *restrict attr,
       void *(*start_rtn)(void *), void *restrict arg);
a.     pthread_t *restrict tidp:返回最后创建出来的Thread的Thread ID
b.     const pthread_attr_t *restrict attr:指定线程的Attributes,后面会讲道,现在可以用NULL
c.     void *(*start_rtn)(void *):指定线程函数指针,该函数返回一个void *,参数也为void*
d.     void *restrict arg:传入给线程函数的参数
e.     返回错误值。
2.     pthread函数在出错的时候不会设置errno,而是直接返回错误值
3.     在Linux系统下面,在老的内核中,由于Thread也被看作是一种特殊,可共享地址空间和资源的Process,因此在同一个Process中创建的不同Thread具有不同的Process ID(调用getpid获得)。 而在新的2.6内核之中,Linux采用了NPTL(Native POSIX Thread Library)线程模型(可以参考http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library http://www-128.ibm.com/developerworks/linux/library/l-threading.html?ca=dgr-lnxw07LinuxThreadsAndNPTL),在该线程模型下同一进程下不同线程调用getpid返回同一个PID。
4.     不能对创建的新线程和当前创建者线程的运行顺序作出任何假设
5 Thread Termination
1.     exit, _Exit, _exit用于中止当前进程,而非线程
2.     中止线程可以有三种方式:
a.     在线程函数中return
b.     被同一进程中的另外的线程Cancel掉
c.     线程调用pthread_exit函数
3.     pthread_exit和pthread_join函数的用法:
a.     线程A调用pthread_join(B, &rval_ptr),被Block,进入Detached状态(如果已经进入Detached状态,则pthread_join函数返回EINVAL)。如果对B的结束代码不感兴趣,rval_ptr可以传NULL。
b.     线程B调用pthread_exit(rval_ptr),退出线程B,结束代码为rval_ptr。注意rval_ptr指向的内存的生命周期,不应该指向B的Stack中的数据。
c.     线程A恢复运行,pthread_join函数调用结束,线程B的结束代码被保存到rval_ptr参数中去。如果线程B被Cancel,那么rval_ptr的值就是PTHREAD_CANCELLED。
两个函数原型如下:

#i nclude <pthread.h>
 
void pthread_exit(void *rval_ptr);
 
int pthread_join(pthread_t thread, void **rval_ptr);
4.     一个Thread可以要求另外一个Thread被Cancel,通过调用pthread_cancel函数:

#i nclude <pthread.h>
 
void pthread_cancel(pthread_t tid)
该函数会使指定线程如同调用了pthread_exit(PTHREAD_CANCELLED)。不过,指定线程可以选择忽略或者进行自己的处理,在后面会讲到。此外,该函数不会导致Block,只是发送Cancel这个请求。
5.     线程可以安排在它退出的时候,某些函数自动被调用,类似atexit()函数。需要调用如下函数:

#i nclude <pthread.h>
 
void pthread_cleanup_push(void (*rtn)(void *), void *arg);
void pthread_cleanup_pop(int execute);
这两个函数维护一个函数指针的Stack,可以把函数指针和函数参数值push/pop。执行的顺序则是从栈顶到栈底,也就是和push的顺序相反。
在下面情况下pthread_cleanup_push所指定的thread cleanup handlers会被调用:
a.     调用pthread_exit
b.     相应cancel请求
c.     以非0参数调用pthread_cleanup_pop()。(如果以0调用pthread_cleanup_pop(),那么handler不会被调用
有一个比较怪异的要求是,由于这两个函数可能由宏的方式来实现,因此这两个函数的调用必须得是在同一个Scope之中,并且配对,因为在pthread_cleanup_push的实现中可能有一个{,而pthread_cleanup_pop可能有一个}。因此,一般情况下,这两个函数是用于处理意外情况用的,举例如下:

void *thread_func(void *arg)
{
    pthread_cleanup_push(cleanup, "handler")
 
    // do something
 
    Pthread_cleanup_pop(0);
    return((void *)0)
}
 
6.     进程函数和线程函数的相关性:

Process Primitive
Thread Primitive
Description
fork
pthread_create
创建新的控制流
exit
pthread_exit
退出已有的控制流
waitpid
pthread_join
等待控制流并获得结束代码
atexit
pthread_cleanup_push
注册在控制流退出时候被调用的函数
getpid
pthread_self
获得控制流的id
abort
pthread_cancel
请求非正常退出
7.     缺省情况下,一个线程A的结束状态被保存下来直到pthread_join为该线程被调用过,也就是说即使线程A已经结束,只要没有线程B调用pthread_join(A),A的退出状态则一直被保存。而当线程处于Detached状态之时,党线程退出的时候,其资源可以立刻被回收,那么这个退出状态也丢失了。在这个状态下,无法为该线程调用pthread_join函数。我们可以通过调用pthread_detach函数来使指定线程进入Detach状态:

#i nclude <pthread.h>
 
int pthread_detach(pthread_t tid);
通过修改调用pthread_create函数的attr参数,我们可以指定一个线程在创建之后立刻就进入Detached状态
6 Thread Synchronization
1.     互斥量:Mutex
a.     用于互斥访问
b.     类型:pthread_mutex_t,必须被初始化为PTHREAD_MUTEX_INITIALIZER(用于静态分配的mutex,等价于pthread_mutex_init(…, NULL))或者调用pthread_mutex_init。Mutex也应该用pthread_mutex_destroy来销毁。这两个函数原型如下:(attr的具体含义下一章讨论)

#i nclude <pthread.h>
 
int pthread_mutex_init(
       pthread_mutex_t *restrict mutex,
       const pthread_mutexattr_t *restrict attr)
 
int pthread_mutex_destroy(pthread_mutex_t *mutex);
c.     pthread_mutex_lock用于Lock Mutex,如果Mutex已经被Lock,该函数调用会Block直到Mutex被Unlock,然后该函数会Lock Mutex并返回。pthread_mutex_trylock类似,只是当Mutex被Lock的时候不会Block,而是返回一个错误值EBUSY。pthread_mutex_unlock则是unlock一个mutex。这三个函数原型如下:

#i nclude <pthread.h>
 
int pthread_mutex_lock(pthread_mutex_t *mutex);
 
int pthread_mutex_trylock(pthread_mutex_t *mutex);
 
int pthread_mutex_unlock(pthread_mutex_t *mutex);
 
2.     读写锁:Reader-Writer Locks
a.     多个线程可以同时获得读锁(Reader-Writer lock in read mode),但是只有一个线程能够获得写锁(Reader-writer lock in write mode)
b.     读写锁有三种状态
                                          i.     一个或者多个线程获得读锁,其他线程无法获得写锁
                                         ii.     一个线程获得写锁,其他线程无法获得读锁
                                        iii.     没有线程获得此读写锁
c.     类型为pthread_rwlock_t
d.     创建和关闭方法如下:

#i nclude <pthread.h>
 
int pthread_rwlock_init(
       pthread_rwlock_t *restrict rwlock,
       const pthread_rwlockattr_t *restrict attr)
 
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
e.     获得读写锁的方法如下:

#i nclude <pthread.h>
 
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
 
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
 
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
 
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
 
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
 
pthread_rwlock_rdlock:获得读锁
pthread_rwlock_wrlock:获得写锁
pthread_rwlock_unlock:释放锁,不管是读锁还是写锁都是调用此函数
注意具体实现可能对同时获得读锁的线程个数有限制,所以在调用pthread_rwlock_rdlock的时候需要检查错误值,而另外两个pthread_rwlock_wrlock和pthread_rwlock_unlock则一般不用检查,如果我们代码写的正确的话。
3.     Conditional Variable:条件
a.     条件必须被Mutex保护起来
b.     类型为:pthread_cond_t,必须被初始化为PTHREAD_COND_INITIALIZER(用于静态分配的条件,等价于pthread_cond_init(…, NULL))或者调用pthread_cond_init

#i nclude <pthread.h>
 
int pthread_cond_init(
       pthread_cond_t *restrict cond,
       const pthread_condxattr_t *restrict attr)
 
int pthread_cond_destroy(pthread_cond_t *cond);
 
c.     pthread_cond_wait函数用于等待条件发生(=true)。pthread_cond_timedwait类似,只是当等待超时的时候返回一个错误值ETIMEDOUT。超时的时间用timespec结构指定。此外,两个函数都需要传入一个Mutex用于保护条件

#i nclude <pthread.h>
 
int pthread_cond_wait(
       pthread_cond_t *restrict cond,
       pthread_mutex_t *restrict mutex);
 
int pthread_cond_timedwait(
       pthread_cond_t *restrict cond,
       pthread_mutex_t *restrict mutex,
       const struct timespec *restrict timeout);
 
d.     timespec结构定义如下:

struct timespec {
       time_t tv_sec;       /* seconds */
       long   tv_nsec;      /* nanoseconds */
};
注意timespec的时间是绝对时间而非相对时间,因此需要先调用gettimeofday函数获得当前时间,再转换成timespec结构,加上偏移量。
e.     有两个函数用于通知线程条件被满足(=true):

#i nclude <pthread.h>
 
int pthread_cond_signal(pthread_cond_t *cond);
 
int pthread_cond_broadcast(pthread_cond_t *cond);
两者的区别是前者会唤醒单个线程,而后者会唤醒多个线程。
 

 
 
此文章被以下用户所推荐:
 
同意,不要再提了。今天南京晨报A3版调查报告已有说法。
[ 2007-9-5 3:58:13 | By: yaopol(游客) ]
 
yaopol(游客) 同意,不要再提了。今天南京晨报A3版调查报告已有说法。
还表扬了我们车友会呢。
 
 
 
Re:pthread多线程编程整理(一)
[ 2007-9-1 11:32:37 | By: overcomeunicom(游客) ]
 
overcomeunicom(游客)您好:
在网上搜到这个,进来看了看,请问
typedef union
{
# if __WORDSIZE == 64
struct
{
int __lock;
unsigned int __nr_readers;
unsigned int __readers_wakeup;
unsigned int __writer_wakeup; //这代表什么?
unsigned int __nr_readers_queued;
unsigned int __nr_writers_queued;
int __writer;
int __pad1;
unsigned long int __pad2;
unsigned long int __pad3;
/* FLAGS must stay at this position in the structure to maintain
binary compatibility. */
unsigned int __flags;
} __data;
# else
struct
{
int __lock;
unsigned int __nr_readers;
unsigned int __readers_wakeup;
unsigned int __writer_wakeup;
unsigned int __nr_readers_queued;
unsigned int __nr_writers_queued;
/* FLAGS must stay at this position in the structure to maintain
binary compatibility. */
unsigned int __flags;
int __writer;
} __data;
# endif
char __size[__SIZEOF_PTHREAD_RWLOCK_T];
long int __align;
} pthread_rwlock_t;
我尝试线程获取读锁后不释放,由线程清理函数pthread_cleanup_push调用注册的回调函数释放未释放的写锁,结果该字段不为0,尽管可以工作,但仍感觉不妥,请问怎么解决?
http://www.mcublog.com/blog/user1/5/archives/2007/24235.html
7 janvier

HOWTO: How to Use PeekMessage() Correctly in Windows--如何正确的使用PeekMessage() (收藏)


HOWTO: How to Use PeekMessage() Correctly in Windows

SUMMARY

In the Windows environment, many applications use a PeekMessage() loop to perform background processing. Such applications must allow the Windows system to enter an idle state when their background processing is complete. Otherwise, system performance, "idle-time" system processes such as paging optimizations, and power management on battery-powered systems will be adversely affected.

While an application is in a PeekMessage() loop, the Windows system cannot go idle. Therefore, an application should not remain in a PeekMessage() loop after its background processing has completed.

NOTE: The PeekMessage method described in this article is only needed if your application is a 32-bit application for Windows and is, for some reason, unable to create threads and perform background processing.

MORE INFORMATION

Many Windows-based applications use PeekMessage() to retrieve messages while they are in the middle of a long process, such as printing, repaginating, or recalculating, that must be done "in the background." PeekMessage() is used in these situations because, unlike GetMessage(), it does not wait for a message to be placed in the queue before it returns.

An application should not call PeekMessage() unless it has background processing to do between the calls to PeekMessage(). When an application is waiting for an input event, it should call GetMessage() or WaitMessage().

Remaining in a PeekMessage() loop when there is no background work causes system performance problems. A program in a PeekMessage() loop continues to be rescheduled by the Windows scheduler, consuming CPU time and taking time away from other processes.

In enhanced mode, the Virtual Machine (VM) in which Windows is running will not appear to be idle as long as an application is calling the PeekMessage function. Therefore, the Windows VM will continue to receive a considerable fraction of CPU time.

Many power management methods employed on laptop and notebook computers are based on the system going idle when there is no processing to do. An application that remains in a PeekMessage() loop will make the system appear busy to power management software, resulting in excessive power consumption and shortening the time that the user can run the system.

In the future, the Windows system will make more and more use of idle time to do background processing, which is designed to optimize system performance. Applications that do not allow the system to go idle will adversely affect the performance of these techniques.

All these problems can be avoided by calling the PeekMessage() function only when there is background work to do, and calling GetMessage() or WaitMessage() when there is no background work to do.

For example, consider the following PeekMessage() loop. If there is no background processing to do, this loop will continue to run without waiting for messages, preventing the system from going idle and causing the negative effects described above.
   // This PeekMessage loop will NOT let the system go idle.
   for( ;; )
   {
      while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE ))
      {
         if (msg.message == WM_QUIT)
            return TRUE;
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
      BackgroundProcessing();
    }
This loop can be rewritten in two ways, as shown below. Both of the following PeekMessage() loops have two desirable properties:

They process all input messages before performing background processing, providing good response to user input.
The application "idles" (waits for an input message) when no background processing needs to be done.
 

Improved PeekMessage Loop 1

   // Improved PeekMessage() loop
   
for(;;)
   {
      while (PeekMessage(&msg, NULL
, 0, 0, PM_REMOVE))
      {
         if (msg.message == WM_QUIT)
            return TRUE;
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
      if (IfBackgroundProcessingRequired())
         BackgroundProcessing();
      else
         WaitMessage(); // Will not return until a message is posted.

   }	

Improved PeekMessage Loop 2

   // Another improved PeekMessage() loop
   
for (;;)
   {
      for (;;)
      {
         if (IfBackgroundProcessingRequired())
         {
            if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
               break;
         }
         else
            GetMessage(&msg, NULL, 0, 0, 0);
         if (msg.message == WM_QUIT)
            return TRUE ;
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
      BackgroundProcessing();
   }		
Note that calls to functions such as IsDialogMessage() and TranslateAccelerator() can be added to these loops as appropriate.

There is one case in which the loops above need additional support: if the application waits for input from a device (for example, a fax board) that does not send standard Windows messages. For the reasons outlined above, a Windows-based application should not use a PeekMessage() loop to continuously poll the device. Rather, implement an Interrupt Service Routine (ISR) in a Dynamic-Link Library (DLL). When the ISR is called, the DLL can use the PostMessage function to inform the application that the device requires service. DLL functions can safely call the PostMessage() function because the PostMessage() function is reentrant.