介绍
在现代操作系统中,每个进程都有自己的地址空间和一个控制线程。然而,在实践中,我们经常面临需要在单个进程中执行多个并发任务并访问相同流程组件的情况:结构、打开文件描述符等。
在任何情况下组织多线程模型都需要同时访问相同的资源。本文提供有关 Windows 和 Linux 操作系统中线程的一般信息,然后介绍同步机制[1]阻止访问共享资源。
对于那些处理从一个系统移植到另一个系统的应用程序,或者在一个系统中创建多线程应用程序并想知道它在另一个系统中的实际实现方式的人来说,本文将很有趣。对于那些从未使用多个线程编写应用程序但计划在未来这样做的人,本文也将很有用。
螺纹概念
这些线程需要做什么?为什么我们不能只创建流程?后一种范式已经工作了很多年,但流程创建有一些缺点,只有以下几个例子:
流程创建操作是资源密集型操作。
进程需要复杂的机制来访问相同的资源(命名或未命名管道、消息队列、套接字等),而线程会自动访问相同的地址空间。
多线程进程的性能高于单线程进程。
多线程允许多个线程作为一个进程的一部分执行。带有线程的编程模型为开发人员提供了同时执行的舒适抽象。带线程的程序的优点之一是它在具有多核处理器的计算机上运行得更快。线程在创建时几乎不使用资源,也不使用资源访问机制等其他插件;此外,线程的性能和应用程序交互性更高。除了地址空间,所有线程都使用:
工艺规定
信号处理程序(用于处理信号的设置)
当前目录
用户和组标识符
同时,每个线程都有自己的:
线程标识符
叠
寄存器集
信号掩模
优先权
使用线程的主要函数
在通过 exec 调用启动程序时,将创建一个主线程(初始线程)。辅助线程是通过调用 Linux 的 pthread_create 或 Windows 的 _beginthread(ex) 来创建的。
让我们更仔细地看一下 Linux 的线程创建:
#include
int pthread_create(
pthread_t *tid,
const pthread_attr_t *attr,
void *(*func)(void *),
void *arg
);
/* 成功完成时返回 0,出错时返回正值*/
每个线程都有其标识符 –pthread_t– 和属性:优先级、初始堆栈大小、守护程序功能。创建线程时,必须指示将执行的函数地址 (func) 以及单指针参数 (arg)。Linux 中的线程应显式退出 –pthread_exit通过调用函数 – 或间接退出 – 通过从该函数返回[2]。如果在问题条件下需要将多个参数传递给线程,则必须将结构的地址与参数一起使用。
在Windows中,线程是在_beginthread(ex)或CreateThread函数的帮助下创建的。两者都是运行时调用,它们之间的主要区别在于 CreateThread 是一个“原始”Win32 API,并且_beginthread(ex)反过来调用自身内部的CreateThread。在本文中,我们将讨论_beginthread(ex)函数。_beginthreadex的语法如下:
uintptr_t _beginthreadex(
void *security,
unsigned stack_size,
unsigned(__stdcall *start_address)(void *),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
可以观察到,在pthread_create和_beginthreadex调用之间有一些模糊的相似之处;但是,也存在差异。?hus, in Windows:security– 指向结构的指针SECURITY_ATTRIBUTES,thrdaddr– 指向接收线程标识符的 32 位变量。
让我们考虑以下线程创建示例[3]:
#include
#ifdef __PL_WINDOWS__
#include
#endif //__PL_WINDOWS__
#ifdef __PL_LINUX__
#include
#endif //__PL_LINUX__
#define STACK_SIZE_IN_BYTES (2097152) //2MB
#ifdef __PL_WINDOWS__
unsigned int __stdcall process_command_thread(void) {
#endif //__PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
void *process_command_thread(void *p) {
#endif //(__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
printf(“Hello from process command threadn”);
return 0;
}
int main(int argc, char *argv[])
{
#ifdef __PL_WINDOWS__
DWORD process_command_thread_id;
HANDLE h_process_command_thread;
h_process_command_thread = (HANDLE)_beginthreadex(
NULL,
STACK_SIZE_IN_BYTES,
process_command_thread,
NULL,
0,
(unsigned long *)&process_command_thread_id
);
if (h_process_command_thread == NULL)
return -1;
#endif //__PL_WINDOWS__
#ifdef __PL_LINUX__
pthread_t h_process_command_thread;
int h_process_command_thread_initialized;
int ret;
ret = pthread_create(
&h_process_command_thread,
NULL,
process_command_thread,
NULL
);
if (ret != 0)
return -1;
h_process_command_thread_initialized = 1;
#endif // __PL_LINUX__
printf(“Hello from main threadn”);
return 0;
}
输出将如下所示:
Linux目录窗户
[root@localhost~]# 。/进程
来自主线程的你好
[root@localhost ~]#C:》进程.exe
来自主线程的你好
C:》
很容易注意到process_command_thread不是以可视方式运行的。当用于线程管理的内部结构由pthread_createor_beginthreadex函数初始化时,主线程完成执行。我们可以预期在为 Linux 调用pthread_join后线程退出。
int pthread_join(pthread_t tid, void **retval);
线程可以是可连接的(默认情况下)或分离的。当可连接线程终止时,信息(标识符、终止状态、线程计数器等)将保留,直到调用pthread_join。
在Windows操作系统中,其中一个等待功能可能被视为类似于pthread_join。等待函数系列允许线程中断其执行并等待资源释放。让我们看一下pthread_join的类似物,即WaitForSingleObject:
DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);
调用此函数时,第一个参数 hObject 标识内核对象。此对象可能处于以下两种状态之一:“空闲”或“忙碌”。
第二个参数 dwMilliseconds 指示线程准备好等待释放对象的毫秒数。
以下示例说明了pthread_joinWaitForSingleObject调用:
#ifdef __PL_WINDOWS__
DWORD status = WaitForSingleObject(
h_process_command_thread,
INFINITE
);
switch (status) {
case WAIT_OBJECT_0:
// The process terminated
break;
case WAIT_TIMEOUT:
// The process did not terminate within timeout
break;
case WAIT_FAILED:
// Bad call to function
break;
}
#endif //__PL_WINDOWS__
#ifdef __PL_LINUX__
int status = pthread_join(
h_process_command_thread,
NULL
);
switch (status) {
case 0:
// The process terminated
break;
case default:
// Bad call to function
break;
}
#endif //__PL_LINUX__
#ifdef __PL_WINDOWS__
//Windows code
#endif //__PL_WINDOWS__
#ifdef __PL_LINUX__
//Code for UNIX OS systems
#endif //__PL_LINUX__
对于 Linux,本文介绍了由 POSIX.1-2001 标准(称为“pthreads”)定义的线程接口。
本文稍后将介绍线程退出。
在此示例中,与本文中的其他示例一样,Linux 和 Windows 的代码库都是单一的。区别在于编译条件:
#ifdef __PL_WINDOWS__
//Windows code
#endif //__PL_WINDOWS__
#ifdef __PL_LINUX__
//Code for UNIX OS systems
#endif //__PL_LINUX__
全部0条评论
快来发表一下你的评论吧 !