0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux和Windows系统中的多线程应用程序并行性

星星科技指导员 来源:嵌入式计算设计 作者:Eduard Trunov 2022-11-30 15:27 次阅读

多线程应用程序并行性

通常,在多线程应用程序中有两种相互关联但不同的现象:并发性和并行性。

并发是两个或多个线程在执行中重叠的能力。

并行性是同时执行两个或多个线程的能力。

正是并发性导致了流式处理中的大多数复杂性 - 线程可以以不可预测的顺序执行。在线程共享资源的情况下,这无疑会导致竞争条件。

术语争用条件通常是指对两个和多个线程的共享资源的不同步访问导致错误的程序行为的情况。

让我们看一个比赛的例子。

如今,很难想象我们没有塑料卡的生活。ATM取款很久以前就成为日常工作:插入卡,输入PIN码和所需的金额。如果成功完成,我们将收到计划的现金金额。反过来,银行需要通过以下算法验证资金是否可用:

银行账户上是否至少有 X 个单位的可用货币?

如果是,将帐户余额减少 X 值,向用户分配 X 个货币单位。

否则,将生成错误消息。

具有争用条件的代码示例:

int cash_out(struct account *ac, int amount) {

const int balance = ac->balance;

if (balance < amount)

return -1;

ac->balance = balance - amount;

discard_money_routine(amount);

return 0;

}

当在线支付购买并“同时”从 ATM 提取现金时,可能会出现种族。

为了避免比赛,有必要对代码进行以下更新:

int cash_out(struct account *ac, int amount) {

lock();

const int balance = ac->balance;

if (balance < amount)

return -1;

ac->balance = balance - amount;

unlock();

discard_money_routine(amount);

return 0;

}

在Windows操作系统中,需要独占访问某些共享数据的代码区域称为“关键部分”。

用于处理关键部分的结构类型为CRITICAL_SECTION。让我们回顾一下它的字段:

typedef struct _RTL_CRITICAL_SECTION {

PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

//

// The following three fields control entering and exiting the critical

// section for the resource

//

LONG LockCount;

LONG RecursionCount;

HANDLE OwningThread; // from the thread's ClientId->UniqueThread

HANDLE LockSemaphore;

ULONG_PTR SpinCount; // force size on 64-bit systems when packed

} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

尽管CRITICAL_SECTION正式不属于未记录的结构,但微软仍然认为用户无需了解其组织。实际上,它是一种黑匣子。要使用此结构,无需直接使用其字段,而只需通过 Windows 函数,将此结构的相应实例的地址传递给它们。

CRITICAL_SECTION结构通过以下调用初始化:

void 初始化关键部分(PCRITICAL_SECTION 个);

如果我们知道不再需要CRITICAL_SECTION结构,那么我们可以借助以下调用将其删除:

无效删除关键部分(PCRITICAL_SECTION个);

使用共享资源的代码区域应先进行以下调用:

无效的进入临界部分(PCRITICAL_SECTION个);

我们可以使用以下命令代替EnterCriticalSection:

bool 尝试输入关键部分(PCRITICAL_SECTION 个);

TryEnterCriticalSection允许线程检查资源可访问性,并在无法访问时参与另一个活动。在成功的情况下(函数返回TRUE),很明显结构元素已更新,资源已锁定。

在使用共享资源的代码区域末尾,应始终存在以下调用:

void LeaveCriticalSection(PCRITICAL_SECTION pcs);


LeaveCriticalSection检查CRITICAL_SECTION结构元素,并将资源锁定计数器 (LockCount) 减少 1。

类似于 Linux 操作系统中的CRITICAL_SECTION是可变互斥pthread_mutex_t。在使用之前,需要初始化此变量 – 写入常量PTHREAD_MUTEX_INITIALIZER的值或调用pthread_mutex_init函数。

#include

int pthread_mutex_init(pthread_mutex_t *restrict mutex,

const pthread_mutexattr_t *restrict attr);

要使用默认属性值初始化互斥锁,必须将NULL传递给attr属性。可以在帮助页面上找到特定的互斥锁属性值。

可以通过以下调用删除互斥锁:

int pthread_mutex_destroy(pthread_mutex_t *mutex);


通过调用pthread_mutex_lock函数锁定互斥锁:

int pthread_mutex_lock(pthread_mutex_t *mutex);


如果互斥锁已被锁定,则调用线程将被阻塞,直到释放互斥锁。互斥锁在pthread_mutex_unlock功能的帮助下解锁:

int pthread_mutex_unlock(pthread_mutex_t *mutex);


如果我们想检查资源的可访问性,那么我们可以使用pthread_mutex_trylock函数:

int pthread_mutex_trylock(pthread_mutex_t *mutex);


如果互斥锁被锁定,上述函数将返回EBUSY。

所有用于处理互斥锁的函数在成功时返回 0,在失败时返回错误代码。

让我们总结一下。在 Windows 操作系统中,要使用共享资源,必须使用关键部分和特殊类型的CRITICAL_SECTION。在 Linux 操作系统中,我们可以出于相同目的使用pthread_mutex_t类型的互斥体。

同步功能记录在表 4 中。

窗口函数 Linux函数
初始化关键部分 pthread_mutex_init()
进入关键部分 pthread_mutex_lock()
离开关键部分 pthread_mutex_unlock()
尝试进入关键部分 pthread_mutex_trylock()
删除关键部分 pthread_mutex_destroy()

表 4.共享资源的同步功能。

螺纹端接

在实践中,需要编写线程终止的情况之一是海量数据处理。当主线程向所有线程发出退出信号,但其中一个线程仍在处理信息时,可能会出现这种情况。如果与信息丢失相比,及时性是应用程序性能的更高优先级因素,则需要退出线程并释放系统资源。本节将介绍退出线程的方法。

线程可以通过以下方式退出:

线程函数返回

线程调用 ExitThread 函数

进程的任何线程都调用 TerminateThread 函数

进程的任何线程都调用 ExitProcess 函数

让我们仔细看看其中的每一个。

线程函数返回。

干净代码的一个很好的例子是设计线程函数,以便线程仅在函数返回后终止。在 Windows 操作系统中,这种线程终止方式保证正确清理线程拥有的资源。在 Linux 操作系统中,在线程可连接的情况下,必须调用其中一个连接函数。在一般情况下,会发生以下情况:

系统正确释放线程占用的资源。

系统设置线程退出代码。

此内核对象 «thread» 的用户计数器减少 1。

在 Windows 操作系统中,可以通过调用以下内容来强制终止线程:

void ExitThread(DWORD dwExitCode);

线程退出代码值将添加到dwExitCode参数中。很容易注意到该函数没有返回值,因为在调用该函数后,线程将不复存在。

在Linux操作系统中,有一个完整的ExitThread模拟

void pthread_exit(void *rval_ptr);


参数rval_ptr表示包含返回值的非类型指针。此指针可由调用pthread_join函数的其他进程线程获取。

函数调用pthread_join将线程带到分离状态。此状态允许赢回线程资源。如果线程已处于分离状态,则调用pthread_join的线程将收到ESRCH错误代码。有时,当使用第二个非NULL参数调用pthread_join时,可能会输出分段错误错误。

进程的任何线程都调用 TerminateThread 函数。

一个线程可以传递请求以强制终止同一进程中的另一个线程。在Windows操作系统中,这是在以下功能的帮助下组织的:

bool TerminateThread(

HANDLE hThread,

DWORD dwExitCode

);

上述函数从任何其他线程终止了 hThread线程。您可以向dwExitCode参数添加一个值,系统将视之为线程退出代码。线程被杀死后,此内核对象 «thread» 的用户计数器将减少 1。

在 Linux 操作系统中,当一个线程可以通过调用pthread_cancel函数传递强制终止同一进程中另一个线程的请求时,可以实现类似的功能:

int pthread_cancel(pthread_t tid);

此函数需要与pthread_setcancelstate和pthread_setcanceltype函数结合使用。如果使用pthread_cancel,rval_ptr将被PTHREAD_CANCELED

让我们仔细看看erminateThread和 Linux 操作系统中的类似操作:

#ifdef __PL_WINDOWS__

BOOL bret = FALSE;

bret = TerminateThread(h, x);

#endif //__PL_WINDOWS__

#ifdef __PL_LINUX__

int iret = 0, bret;

iret = syscall(SYS_tkill,tid, 0);

if (iret == 0) {

iret = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);

if (iret != 0) {

bret = FALSE;

}

else {

iret = pthread_cancel(h);

if (iret == 0 || iret == ESRCH) {

bret = TRUE;

} else {

wait_thread:

clock_gettime(CLOCK_REALTIME, &wait_time);

ADD_MS_TO_TIMESPEC(wait_time, 1000); //1000 ms

iret = pthread_timedjoin_np(h, NULL, &wait_time);

switch (iret) {

case 0:

bret = TRUE;

break;

case ETIMEDOUT:

if (retries_count++ < 5) // 5 Attempts

{

goto wait_thread;

}

bret = FALSE;

break;

default:

bret = FALSE;

break;

}

}

(void)pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);

}

}

else {

bret = TRUE;

}

#endif //__PL_LINUX__

审核编辑:郭婷

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    11313

    浏览量

    209748
  • WINDOWS
    +关注

    关注

    4

    文章

    3552

    浏览量

    88824
  • 线程
    +关注

    关注

    0

    文章

    505

    浏览量

    19703
收藏 人收藏

    评论

    相关推荐

    socket 多线程编程实现方法

    是指在同一个进程运行多个线程,每个线程可以独立执行任务。线程共享进程的资源,如内存空间和文件句柄,但每个线程有自己的
    的头像 发表于 11-12 14:16 381次阅读

    Python多线程和多进程的区别

    Python作为一种高级编程语言,提供了多种并发编程的方式,其中多线程与多进程是最常见的两种方式之一。在本文中,我们将探讨Python多线程与多进程的概念、区别以及如何使用线程池与进
    的头像 发表于 10-23 11:48 413次阅读
    Python<b class='flag-5'>中</b><b class='flag-5'>多线程</b>和多进程的区别

    一文掌握Python多线程

    使用线程可以把占据长时间的程序的任务放到后台去处理。
    的头像 发表于 08-05 15:46 872次阅读

    多线程设计模式到对 CompletableFuture 的应用

    最近在开发 延保服务 频道页时,为了提高查询效率,使用到了多线程技术。为了对多线程方案设计有更加充分的了解,在业余时间读完了《图解 Java 多线程设计模式》这本书,觉得收获良多。本篇文章将介绍其中
    的头像 发表于 06-26 14:18 370次阅读
    从<b class='flag-5'>多线程</b>设计模式到对 CompletableFuture 的应用

    bootloader开多线程做引导程序,跳app初始化后直接进hardfualt,为什么?

    如标题,想做一个远程升级的项目,bootloader引导区域和app都是开多线程跑的,就是自己写了个小的任务调度器,没什么功能主要是想让程序快速的响应,延时不会对其他程序造成堵塞,程序
    发表于 04-18 06:07

    鸿蒙OS开发实例:【ArkTS类库多线程CPU密集型任务TaskPool】

    CPU密集型任务是指需要占用系统资源处理大量计算能力的任务,需要长时间运行,这段时间会阻塞线程其它事件的处理,不适宜放在主线程进行。例如图像处理、视频编码、数据分析等。 基于多线程
    的头像 发表于 04-01 22:25 852次阅读
    鸿蒙OS开发实例:【ArkTS类库<b class='flag-5'>多线程</b>CPU密集型任务TaskPool】

    鸿蒙APP开发:【ArkTS类库多线程】TaskPool和Worker的对比

    TaskPool(任务池)和Worker的作用是为应用程序提供一个多线程的运行环境,用于处理耗时的计算任务或其他密集型任务。可以有效地避免这些任务阻塞主线程,从而最大化系统的利用率,降
    的头像 发表于 03-26 22:09 659次阅读
    鸿蒙APP开发:【ArkTS类库<b class='flag-5'>多线程</b>】TaskPool和Worker的对比

    鸿蒙原生应用开发-ArkTS语言基础类库多线程TaskPool和Worker的对比(一)

    TaskPool(任务池)和Worker的作用是为应用程序提供一个多线程的运行环境,用于处理耗时的计算任务或其他密集型任务。可以有效地避免这些任务阻塞主线程,从而最大化系统的利用率,降
    发表于 03-25 14:11

    java实现多线程的几种方式

    Java实现多线程的几种方式 多线程是指程序包含了两个或以上的线程,每个线程都可以
    的头像 发表于 03-14 16:55 726次阅读

    Linux的用途及优势在哪里?

    Linux是一套免费使用和自由传播的类Unix操作系统,是一个多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程
    发表于 03-07 11:10 1510次阅读
    <b class='flag-5'>Linux</b>的用途及优势在哪里?

    python5种线程锁盘点

    线程安全是多线程或多进程编程的一个概念,在拥有共享数据的多条线程并行执行的程序
    发表于 03-07 11:08 1603次阅读
    python<b class='flag-5'>中</b>5种<b class='flag-5'>线程</b>锁盘点

    AT socket可以多线程调用吗?

    请问AT socket 可以多线程调用吗? 有互锁机制吗,还是要自己做互锁。
    发表于 03-01 08:22

    什么是多核多线程?多核多线程如何提高程序的运行效率?

    线程无法充分利用多核处理器的并行计算能力。
    的头像 发表于 02-20 10:22 1387次阅读

    linux多线程编程实例

    linux线程
    的头像 发表于 02-15 21:16 479次阅读
    <b class='flag-5'>linux</b><b class='flag-5'>多线程</b>编程实例

    linuxwindows的区别 linux系统一般用来干嘛

    LinuxWindows是两种不同的操作系统,有着不同的设计理念和用途。本文将对LinuxWindows的区别进行详细分析,并介绍
    的头像 发表于 02-05 14:06 991次阅读