多线编程与资源同步
在Windows下,主线程退出后,子线程也会被关闭;
在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程
3.2.1创建线程
Linux 线程的创建
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
void * threadfunc ( void * arg)
{
while ( 1 )
{
sleep ( 1 ) ;
printf ( "I am a new thread!!!\n" ) ;
}
return NULL ;
}
int main ( )
{
pthread_t threadid;
pthread_create ( & threadid, NULL , threadfunc, NULL ) ;
while ( 1 )
{
}
return 0 ;
}
Windows CRT提供的线程创建函数
#include <process.h>
#include <stdio.h>
unsigned int __stdcall threadfun ( void * args)
{
while ( true )
{
printf ( "I am new thread!" ) ;
}
return 0 ;
}
int main ( int argc, char * argv[ ] )
{
unsigned int threadid;
_beginthreadex ( 0 , 0 , threadfun, 0 , 0 , & threadid) ;
while ( true )
{
}
return 0 ;
}
C++ 提供的std::thread
类
#include <iostream>
#include <thread>
void threadproc1 ( )
{
while ( true )
{
printf ( "I am aNew Thread!!" ) ;
}
}
void threadproc2 ( int a, int b)
{
while ( true )
{
printf ( "I an Thread2" ) ;
}
}
int main ( )
{
std:: thread t1 ( threadproc1) ;
std:: thread t2 ( threadproc2, 1 , 2 ) ;
while ( true )
{
}
}
这种方法容易出错,原因如下:
#include <iostream>
#include <thread>
void threadproc1 ( )
{
while ( true )
{
printf ( "I am aNew Thread!!" ) ;
}
}
void func ( )
{
std:: thread t ( threadproc1) ;
}
int main ( )
{
func ( ) ;
while ( true )
{
}
}
这段代码实际上试运行不了的,在vs2019 release模式下结果:
在func函数调用完成后,func中的局部变量t被销毁,而此时线程函数仍然在运行.所以使用std::thread
创建线程必须保证线程函数运行期间,线程对象始终有效!!
当然,我们也可以使用detach
方法解决这个问题,使线程函数和线程对象脱离
. . .
void func ( )
{
std:: thread t ( threadproc1) ;
t. detach ( ) ;
}
. . .
这样就可以运行了,但是不推进这样做,因为我们需要线程对象对线程进行管理
3.2.2线程ID 下面介绍一下Linux系统线程ID本质
Linux系统线程ID本质
在Linux系统中有三种方法可以获取一个线程的ID
调用pthread_create
函数时,可以通过第一个参数获取线程ID 在需要获取ID的线程中调用pthread_self
函数获取 通过系统调用获取线程ID 其中,方法一和方法二获取线程ID的结果都是一样的,都是pthread_t
类型
不同的进程可能有同样的地址内存块(共享内存),所以通过方法一和方法二获取到的线程ID可能不是全系统唯一的,而方法三获取的线程ID是全系统唯一的,就是LWP(轻量级进程)
3.2.3 等待线程结束
在Linux下等待线程结束
Linux 线程库提供了pthread_join
函数,用来等待某线程的退出并接收他的返回值
这种操作被称为汇接(join)
Declared in: pthread. h
static int pthread_join ( pthread_t __th, void * * __thread_return)
参数__th
是需要等待的线程ID;参数__thread__return是输出参数,用于接受被等待线程的退出码,
可以再调用pthread_exit()
时指定退出码
Declared in: pthread. h
static void pthread_exit ( void * __retval)
其中__retval
可以通过__thread_return参数获得
下面来展示一个实例,在程序启动时开启一个工作线程,工作线程将当前系统时间写入一个文件后,主线程等待工作线程退出后,从文件中读取时间,并将其输出
#include <cstdio>
#include <string>
#include <pthread.h>
#include <cstring>
#define TIME_FILENAME "time.txt"
void * fileThreadFunc ( void * arg)
{
time_t now = time ( nullptr ) ;
tm* t = localtime ( & now) ;
char timeStr[ 32 ] = { 0 } ;
snprintf ( timeStr, 32 , "%04d/%02d %02d:%02d:%02d" ,
t- > tm_year+ 1900 ,
t- > tm_mon+ 1 ,
t- > tm_mday,
t- > tm_hour,
t- > tm_min,
t- > tm_sec) ;
FILE* fp = fopen ( TIME_FILENAME, "w" ) ;
if ( fp == nullptr )
{
printf ( "打开文件(time.txt)失败\n" ) ;
return nullptr ;
}
size_t sizeToWrite= strlen ( timeStr) + 1 ;
size_t ret = fwrite ( timeStr, 1 , sizeToWrite, fp) ;
if ( ret != sizeToWrite)
{
printf ( "写入错误!\n" ) ;
}
fclose ( fp) ;
return nullptr ;
}
int main ( )
{
pthread_t fileThreadID;
int ret = pthread_create ( & fileThreadID, nullptr , fileThreadFunc, nullptr ) ;
if ( ret != 0 )
{
printf ( "创建线程失败\n" ) ;
return - 1 ;
}
int * retval;
pthread_join ( fileThreadID, ( void * * ) & retval) ;
FILE* fp = fopen ( TIME_FILENAME, "r" ) ;
if ( fp == nullptr )
{
printf ( "打开文件失败!\n" ) ;
return - 2 ;
}
char buf[ 32 ] = { 0 } ;
int sizeRead = fread ( buf, 1 , 32 , fp) ;
if ( sizeRead == 0 )
{
printf ( "读取文件失败" ) ;
fclose ( fp) ;
return - 3 ;
}
printf ( "Current time is: %s.\n" , buf) ;
fclose ( fp) ;
return 0 ;
}
执行结果如下:
Windows 等待线程结束
在Windows下我们可以使用WaitForSingleObject function (synchapi.h)
和WaitForMultipleObjects function (synchapi.h)
C++11提供的等待线程结束的函数
#include <cstdio>
#include <cstring>
#include <thread>
#define TIME_FILENAME "time.txt"
void fileThreadFunc ( )
{
time_t now = time ( nullptr ) ;
tm* t = localtime ( & now) ;
char timeStr[ 32 ] = { 0 } ;
snprintf ( timeStr, 32 , "%04d/%02d %02d:%02d:%02d" ,
t- > tm_year+ 1900 ,
t- > tm_mon+ 1 ,
t- > tm_mday,
t- > tm_hour,
t- > tm_min,
t- > tm_sec) ;
FILE* fp = fopen ( TIME_FILENAME, "w" ) ;
if ( fp == nullptr )
{
printf ( "打开文件(time.txt)失败\n" ) ;
return ;
}
size_t sizeToWrite= strlen ( timeStr) + 1 ;
size_t ret = fwrite ( timeStr, 1 , sizeToWrite, fp) ;
if ( ret != sizeToWrite)
{
printf ( "写入错误!\n" ) ;
}
fclose ( fp) ;
return ;
}
int main ( )
{
std:: thread t ( fileThreadFunc) ;
if ( t. joinable ( ) ) t. join ( ) ;
FILE* fp = fopen ( TIME_FILENAME, "r" ) ;
if ( fp == nullptr )
{
printf ( "打开文件失败!\n" ) ;
return - 2 ;
}
char buf[ 32 ] = { 0 } ;
int sizeRead = fread ( buf, 1 , 32 , fp) ;
if ( sizeRead == 0 )
{
printf ( "读取文件失败" ) ;
fclose ( fp) ;
return - 3 ;
}
printf ( "Current time is: %s.\n" , buf) ;
fclose ( fp) ;
return 0 ;
}