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
    +关注

    关注

    88

    文章

    11628

    浏览量

    217990
  • WINDOWS
    +关注

    关注

    4

    文章

    3697

    浏览量

    93237
  • 线程
    +关注

    关注

    0

    文章

    508

    浏览量

    20761
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    多线程系统

    ) { /* 无限循环,不能返回 */ for (;;) { /* 线程实体 */ if (flag3) { } } } 相比前后台系统后台顺序执行的程序主体,在
    发表于 12-08 07:55

    Linux多线程对比单线程的优势

    Linux系统线程是操作系统能够进行运算调度的最小单位。线程被包含在进程之中,是进程
    发表于 12-01 06:11

    RK3588核心板/开发板RT-Linux系统实时及硬件中断延迟测试

    延迟检测工具测试Linux系统实时。Cyclictest是一款专门用于测试和评估系统实时(Real-Time)的工具,主要用于测量
    的头像 发表于 11-28 18:57 134次阅读
    RK3588核心板/开发板RT-<b class='flag-5'>Linux</b><b class='flag-5'>系统</b>实时<b class='flag-5'>性</b>及硬件中断延迟测试

    WindowsLinux环境下分别使用Olimex和蜂鸟调试器下载程序

    程序时,可以使用Windows+Olimex+cmd的方式开发和运行程序。 在Windows/Linux
    发表于 10-31 08:26

    rt-thread studio 如何进行多线程编译?

    ,使用的是5800h+32g内存+sn550 ssd,开启16线程编译时cpu的占用率也只能到30%,编译完整个工程需要3分钟 感觉多线程编译设置没有生效,有办法提高编译速度吗 rtthread studio版本是 2.2.9
    发表于 10-11 09:16

    【HZ-T536开发板免费体验】—— linux创建线程

    任务,并行是多个处理器或多核处理器同时执行不同的任务。 Linux系统中进行多线程编程时,会涉及到主线程和子
    发表于 09-01 21:31

    UVC+MSC实现MSC线程未运行的原因?

    线程结构 */ \"30:UVC_app_thread\" , /* 线程 ID 和名称 */ CyCx3UvcAppThread_Entry , /* UVC 应用程序线程入口
    发表于 07-16 07:08

    多线程的安全注意事项

    多线程安全是指多个线程同时访问或修改共享资源时,能够保证程序的正确和可靠。 开发者选择TaskPool或Worker进行
    发表于 06-20 07:49

    龙芯处理器支持WINDOWS吗?

    )。 Windows的适配:微软官方仅支持x86/x64和ARM架构的处理器,未推出针对LoongArch或MIPS的Windows版本。 技术限制 指令集不兼容:Windows系统
    发表于 06-05 14:24

    使用Percepio View免费跟踪工具分析Zephyr应用

    Percepio View免费跟踪工具现在可以针对Zephyr应用程序进行跟踪和可视化分析了。Percepio View可以帮助开发人员理解和调试Zephyr的固定优先级的多线程行为及复杂的
    的头像 发表于 05-27 15:08 589次阅读
    使用Percepio View免费跟踪工具分析Zephyr应用

    Linux主要的性能有哪些?

      什么是Linux?   Linux是一套自由传播的类Unix操作系统,是一个基于posix和unix的多用户、多任务、支持多线程和多cpu的操作
    的头像 发表于 04-30 18:09 491次阅读
    <b class='flag-5'>Linux</b>主要的性能有哪些?

    请问如何在Python实现多线程与多进程的协作?

    () thread.join() process.join() 我的问题是:**如何合理地组合多线程和多进程以获得更好的性能?**特别是在I/O密集型任务和CPU密集型任务混合的情况下,如何避免性能瓶颈,确保程序的高效运行? 希望大家能提供一些解决思路或经验,非常感谢!
    发表于 03-11 06:57

    Linux系统配置优化技巧

    应用系统跑在操作系统上面,系统的性能也关系到应用程序的性能,这里讲一些Linux性能关键的配置信息。
    的头像 发表于 03-04 11:27 744次阅读

    请问rt-thread studio如何进行多线程编译?

    ,使用的是5800h+32g内存+sn550 ssd,开启16线程编译时cpu的占用率也只能到30%,编译完整个工程需要3分钟 感觉多线程编译设置没有生效,有办法提高编译速度吗
    发表于 02-19 08:30

    如何实现Windows应用在Linux系统上的无缝运行

    统信 Windows 应用兼容引擎 V3.0 的推出,让用户可以在 deepin 系统上直接双击.exe文件运行 Windows 应用程序。 近期,我们收到了大家诸多的反馈信息。基于这
    的头像 发表于 12-30 09:51 2540次阅读
    如何实现<b class='flag-5'>Windows</b>应用在<b class='flag-5'>Linux</b><b class='flag-5'>系统</b>上的无缝运行