当前位置: 首页 > news >正文

性能优化-OpenMP基础教程(二)

本文主要介绍OpenMP并行编程技术,编程模型、指令和函数的介绍、以及OpenMP实战的几个例子。希望给OpenMP并行编程者提供指导。

🎬个人简介:一个全栈工程师的升级之路!
📋个人专栏:高性能(HPC)开发基础教程
🎀CSDN主页 发狂的小花
🌄人生秘诀:学习的本质就是极致重复!

目录

一、OpenMP 简介

二、OpenMP 编程模型

1. 指令与库函数

1.1 OpenMP指令格式

1.1.1 并行区域(Parallel Region)

1.1.2 并行构造(Parallel Construct)

1.1.3 任务(Task)

1.1.4 同步(Synchronize)

1.2 OpenMP常用的指令和函数

1.3 OpenMP常用库函数

2. 并行执行

3. 线程管理

4. 同步与通信

5. 调度策略

三、OpenMP编程实战

1 Linux下编译选项

2 C语言 OpenMP 并行化程序示例(包含)

3 C++ OpenMP并行编程示例(包含宏定义#ifdef _OPENMP)

4 OpenMP 多线程性能对比


一、OpenMP 简介

        OpenMP 是一个为共享内存并行计算设计的编程接口,广泛应用于 Fortran、C 和 C++ 语言。它提供了一套编译器指令和库函数,使得开发者能够轻松地编写并行程序。OpenMP 的“fork/join”模型是其中最核心的并行执行模式,其中最初只有一个主线程在运行。当遇到需要并行计算的部分时,主线程会派生出其他线程来执行并行任务。当并行代码执行完毕,派生的线程会退出或挂起,控制权回到主线程。类似与多线程技术。

二、OpenMP 编程模型

1. 指令与库函数

        OpenMP 的基本语法是通过预处理指令 #pragma omp 来实现的。例如,#pragma omp parallel for 用于并行化 for 循环。此外,OpenMP 还提供了一系列的库函数,用于线程的创建、同步等操作。这些库函数和指令使得开发者能够更灵活地控制并行程序的执行。

1.1 OpenMP指令格式

1.1.1 并行区域(Parallel Region)

        用于指定一个代码块,该代码块将在多个线程上并行执行。

#pragma omp parallel
{// 并行执行的代码块
}
1.1.2 并行构造(Parallel Construct)

        用于创建一个新线程并执行指定的代码块

#pragma omp parallel sections
{#pragma omp section{// 线程1执行的代码块}#pragma omp section{// 线程2执行的代码块}
}
1.1.3 任务(Task)

        用于创建一个新任务并在当前线程上执行指定的代码块。

#pragma omp task firstprivate(a, b) shared(c)
{// 任务执行的代码块,使用变量a和b,以及共享变量c
}
1.1.4 同步(Synchronize)

        用于等待所有线程完成指定的任务。

#pragma omp for schedule(static, chunk_size) reduction(+:sum)
for (int i = 0; i < n; i++) {// 循环体,使用变量i和sum
}

1.2 OpenMP常用的指令和函数

  1. parallel:用于指定一个代码段,该代码段将在多个线程上并行执行。

  2. for:用于for循环之前,将循环分配到多个线程中并行执行,必须保证每次循环之间无相关性。

  3. parallel for:parallel 和 for语句的结合,也是用在一个for循环之前,表示for循环的代码将被多个线程并行执行。

  4. sections:用在可能会被并行执行的代码段之前。

  5. parallel sections:parallel和sections两个语句的结合。

  6. critical:用在一段代码临界区之前。

  7. single:用在一段只被单个线程执行的代码段之前,表示后面的代码段将被单线程执行。

  8. flush:用来保证线程的内存临时视图和实际内存保持一致,即各个线程看到的共享变量是一致的。

  9. barrier:用于并行区内代码的线程同步,所有线程执行到barrier时要停止,直到所有线程都执行到barrier时才继续往下执行。

  10. atomic:用于指定一块内存区域被制动更新。

  11. master:用于指定一段代码块由主线程执行。

  12. ordered:用于指定并行区域的循环按顺序执行。

  13. threadprivate:用于指定一个变量是线程私有的。

  14. copyprivate:配合single指令,将指定线程的专有变量广播到并行域内其他线程的同名变量中;

  15. copyin n:用来指定一个threadprivate类型的变量需要用主线程同名变量进行初始化;

  16. default:用来指定并行域内的变量的使用方式,缺省是shared。

1.3 OpenMP常用库函数

        OpenMP库函数是一组用于并行计算的函数,它们可以帮助程序员在C、C++和Fortran等编程语言中实现多线程编程。以下是一些常用的OpenMP库函数:

  1. omp_get_num_threads():返回正在执行的线程数。
  2. omp_get_max_threads():返回支持的最大线程数。
  3. omp_get_thread_num():返回当前线程的编号。
  4. omp_get_num_procs():返回正在执行的程序的处理器数。
  5. omp_set_num_threads():设置并行区域中的线程数。
  6. omp_get_nested():测试当前块是否嵌套在其他并行区域内。
  7. omp_set_nested():设置当前块允许嵌套在其他并行区域内。
  8. omp_get_schedule():获取指定并行区域的调度策略。
  9. omp_set_schedule():设置指定并行区域的调度策略。
  10. omp_get_chunk_size():获取指定并行区域的块大小。
  11. omp_set_chunk_size():设置指定并行区域的块大小。
  12. omp_barrier():在所有线程都到达该点时阻塞所有线程。
  13. omp_critical():创建一个临界区,确保同一时间只有一个线程可以执行该段代码。
  14. omp_atomic():对一个变量进行原子操作,确保多个线程对该变量的操作是有序的。
  15. omp_flush():将缓冲区中的数据立即写入共享内存或设备。
  16. omp_lock_t:用于同步的锁类型。
  17. omp_init_lock():初始化锁对象。
  18. omp_destroy_lock():销毁锁对象。
  19. omp_set_lock():对锁对象加锁。
  20. omp_unset_lock():对锁对象解锁。

2. 并行执行

        OpenMP 提供了多种并行执行的方法,如 parallel for、parallel sections 等。这些方法使得开发者能够将代码块分配给多个线程执行,从而实现更高效的计算。通过合理地划分代码块和选择合适的并行执行方法,开发者可以显著提高程序的性能。

3. 线程管理

        OpenMP 提供了一些指令和函数,如 num_threads、thread_bind 等,用于设置和控制并行区域中的线程数量和绑定策略。这些功能使得开发者能够更好地控制并行程序的执行流程,确保程序的正确性和稳定性。

4. 同步与通信

        为了确保并行执行的正确性,OpenMP 提供了一些同步机制,如 barrier、critical、atomic 等。这些机制确保了线程之间的正确协作和数据一致性。此外,还提供了一些数据传输函数,如 reduction,用于实现线程之间的数据共享和计算结果的汇总。这些同步和通信机制是并行程序中必不可少的部分,它们确保了程序的正确性和可靠性。

5. 调度策略

        OpenMP 支持多种调度策略,如静态调度、动态调度和运行时调度。这些调度策略允许开发者根据需要选择合适的调度策略来优化程序的性能。通过合理地选择调度策略,开发者可以更好地平衡线程的负载和利用系统资源,从而提高程序的执行效率。

三、OpenMP编程实战

1 Linux下编译选项

        Linux下GCC编译器仅仅编译选项增加-fopenmp即可完成对OpenMP的支持。

2 C语言 OpenMP 并行化程序示例(包含<omp.h>)

#include <omp.h>#include <stdio.h>int main() {#pragma omp parallel forfor (int i = 0; i < 10; i++) {printf("Thread %d: %d\n", omp_get_thread_num(), i);}return 0;}

这个程序使用了 #pragma omp parallel for 指令将 for 循环进行并行化。在循环体内部,使用 omp_get_thread_num() 函数获取当前线程的编号,并打印出来。这个示例展示了 OpenMP 的基本用法和并行化效果,通过简单的修改和调整,你可以将其应用于更复杂的并行计算任务。

        运行结果:

        由于使用的电脑是八核的,因此,最多有八个线程,由上述的线程编号可以看出。

如果将上述的循环代码变成8个,如下:

#include <omp.h>#include <stdio.h>int main() {#pragma omp parallel forfor (int i = 0; i < 8; i++) {printf("Thread %d: %d\n", omp_get_thread_num(), i);}return 0;}

        运行结果:

        运行结果是八个线程,线程编号和循环编号相同。

3 C++ OpenMP并行编程示例(包含宏定义#ifdef _OPENMP)

#include <iostream>
#include <omp.h>
int main()
{#ifdef _OPENMP // 如果定义了这个宏std::cout << "Hello, OpenMP!" << std::endl;#pragma omp parallel forfor (int i = 0;i < 8;i++){printf("thread ID is %d i = %d\n",omp_get_thread_num(),i);}#elsestd::cout << "OpenMP is not enabled." << std::endl;#endifreturn 0;
}

        运行结果:

        C++ OpenMP并行编程例子。-fopenmp编译选项开启后,_OPENMP宏被打开。

4 OpenMP 多线程性能对比

#include <stdlib.h>
#include <stdio.h>
#include "omp.h"void test()
{for (int i = 0; i < 80000; i++){//执行代码}
}int main(int argc, char **argv){#ifdef _OPENMPprintf("OpenMP is Enable!\n");#elseprintf("OpenMP is Disable!\n");#endiffloat startTime = omp_get_wtime();//指定2个Thread
#pragma omp parallel for num_threads(2)for (int i = 0; i < 80000; i++){test();}float endTime = omp_get_wtime();printf("2 个Thread,latency: %f\n", endTime - startTime);startTime = endTime;//指定4个Thread
#pragma omp parallel for num_threads(4)for (int i = 0; i < 80000; i++){test();}endTime = omp_get_wtime();printf("4 个Thread,latency: %f\n", endTime - startTime);startTime = endTime;//指定8个Thread
#pragma omp parallel for num_threads(8)for (int i = 0; i < 80000; i++){test();}endTime = omp_get_wtime();printf("8 个Thread,latency: %f\n", endTime - startTime);startTime = endTime;//指定12个Thread#pragma omp parallel for num_threads(10)for (int i = 0; i < 80000; i++){test();}endTime = omp_get_wtime();printf("10 个Thread,latency: %f\n", endTime - startTime);startTime = endTime;//不使用OpenMPfor (int i = 0; i < 80000; i++){test();}endTime = omp_get_wtime();printf("不使用OpenMP Mutil Thread,latency: %f\n", endTime - startTime);startTime = endTime;return 0;
}

        运行结果:

        分析结果可知,随着线程数量的增加运行的时间减少,由于使用的电脑是八核的,因此并行只能同时有八个线程,使用十个线程的运行效率不增反减。

🌈我的分享也就到此结束啦🌈
如果我的分享也能对你有帮助,那就太好了!
若有不足,还请大家多多指正,我们一起学习交流!
📢未来的富豪们:点赞👍→收藏⭐→关注🔍,如果能评论下就太惊喜了
感谢大家的观看和支持!最后,☺祝愿大家每天有钱赚!!!

下一节将继续开展OpenMP编程更加详细的实战。

相关文章:

性能优化-OpenMP基础教程(二)

本文主要介绍OpenMP并行编程技术&#xff0c;编程模型、指令和函数的介绍、以及OpenMP实战的几个例子。希望给OpenMP并行编程者提供指导。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xff08;HPC&am…...

让电脑变得更聪明——用python实现五子棋游戏

作为经典的棋类游戏&#xff0c;五子棋深受大众喜爱&#xff0c;但如果仅实现人与人的博弈&#xff0c;那程序很简单&#xff0c;如果要实现人机对战&#xff0c;教会计算机如何战胜人类&#xff0c;那就不是十分容易的事了。本文我们先从简单入手&#xff0c;完成五子棋游戏的…...

C#-接口

接口 (interface) 定义了一个可由类和结构实现的协定。接口可以包含方法、属性、事件和索引器。接口不提供它所定义的成员的实现 — 它仅指定实现该接口的类或结构必须提供的成员。 接口可支持多重继承。在下面的示例中,接口 IComboBox 同时从 ITextBox 和 IListBox 继承。 i…...

ASP.NET可视化流程设计器源码

源码介绍: ASP.NET可视化流程设计器源码已应用于众多大型企事业单位。拥有全浏览器兼容的可视化流程设计器、表单设计器、基于角色的权限管理等系统开发必须功能&#xff0c;大大为您节省开发时间&#xff0c;是您开发OA.CRM、HR等企事业各种应用管理系统和工作流系统的最佳基…...

景联文科技GPT教育题库:AI教育大模型的强大数据引擎

GPT-4发布后&#xff0c;美国奥数队总教练、卡耐基梅隆大学数学系教授罗博认为&#xff0c;这个几乎是用“刷题”方式喂大的AI教育大模型的到来&#xff0c;意味着人类的刷题时代即将退出历史舞台。 未来教育将更加注重学生的个性化需求和多元化发展&#xff0c;借助GPT和AI教育…...

PHP进阶-实现网站的QQ授权登录

授权登录是站点开发常见的应用场景&#xff0c;通过社交媒体一键授权可以跳过注册站点账户的繁琐操作。本文将讲解如何用PHP实现QQ授权登录。首先&#xff0c;我们需要申请QQ互联开发者账号获得APPID和密钥&#xff1b;接着&#xff0c;我们下载QQ官方SDK&#xff1a;PHP SDK v…...

字节跳动基础架构SRE-Copilot获得2023 CCF国际AIOps挑战赛冠军

近日&#xff0c;2023 CCF国际AIOps挑战赛决赛暨“大模型时代的AIOps”研讨会在北京成功举办&#xff0c;活动吸引了来自互联网、运营商、科研院所、高校、软硬件厂商等领域多名专家学者参与&#xff0c;为智能运维的前沿学术研究、落地生产实践打开了新思路。决赛中&#xff0…...

python moviepy 图文批量合成带字幕口播视频

最近在研究将图片和文本批量合成为带字幕口播视频 主要是基于python的moviepy库 from generator import audio, pics, subs, videodef main():texts_input examplepics_input example# 图片分辨率预处理pics.adjust(pics_input)# 文字转语音audio.text_to_audio(texts_inpu…...

【代码片段】Linux C++打印当前函数调用堆栈

在开发大型项目时&#xff0c;尤其是多线程情况下&#xff0c;一般无法使用断点调试&#xff0c;这时候将当前函数的调用堆栈打印出来是非常有必要和有效的问题排查手段。 这里记录一段Linux环境下&#xff0c;打印函数堆栈的代码。 void get_native_callstack(std::string &a…...

Linux程序、进程以及计划任务(第一部分)

目录 一、程序和进程 1、什么是程序&#xff1f; 2、什么是进程&#xff1f; 3、线程是什么&#xff1f; 4、如何查看是多线程还是单线程 5、进程结束的两种情况&#xff1a; 6、进程的状态 二、查看进程信息的相关命令 1、ps&#xff1a;查看静态的进程统计信息 2、…...

Oracle database 12cRAC异地恢复至单机

环境 rac 环境 byoradbrac Oracle12.1.0.2 系统版本&#xff1a;Red Hat Enterprise Linux Server release 6.5 软件版本&#xff1a;Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit byoradb1&#xff1a;172.17.38.44 byoradb2&#xff1a;172.17.38.4…...

【docker】linux部署docker

简介 首先我需要声明的是&#xff0c;我的系统是centos7&#xff0c;下载工具使用的是yum&#xff1b;在linux上部署docker&#xff0c;之前一直看的是这篇文章Linux之Docker部署&#xff0c;基本上功能方面也都可以使用&#xff0c;部署起来也是比较的简单。首先我先讲述这篇…...

【K8S 云原生】Pod资源限制、Pod容器健康检查(探针)

目录 一、docker的重启方式和K8S重启方式 1、Pod的重启方式&#xff1a; 2、docker的重启策略&#xff1a; 二、yaml文件快速生成&#xff1a; 三、pod的状态&#xff1a; 四、Pod的资源限制 1、限制的方式和种类 2、CPU的限制的格式&#xff1a; 五、K8S拉取镜像的策…...

Python从入门到网络爬虫(模块详解)

模块 我们知道&#xff0c;函数和类都是可以重复调用的代码块。在程序中使用位于不同文件的代码块的方法是&#xff1a;导入 (import) 该对象所在的模块 (mudule)。当程序变得越来越大时&#xff0c;将程序的不同部分根据不同分类方法保存在不同文件中通常会更加方便。 导入模…...

[大厂实践] 无停机迁移大规模关键流量(下)

在系统升级、迁移的过程中&#xff0c;如何验证系统逻辑、性能正确无误&#xff0c;是一个很大的挑战。这一系列介绍了Netflix通过重放流量测试解决这一挑战的实践。原文: Migrating Critical Traffic At Scale with No Downtime — Part 2 想象一下&#xff0c;你被心爱的Netf…...

VMware Workstation虚拟机CentOS 7.9 配置固定ip的步骤

VMware Workstation虚拟机CentOS7.9配置固定ip的步骤 编辑虚拟机 打开VMware Workstation。 选择要配置的虚拟机&#xff0c;但不要启动它。 点击“编辑虚拟机设置”&#xff08;Edit virtual machine settings&#xff09;。 选择“网络适配器”&#xff08;Network Adapter&…...

构建自己的私人GPT

创作不易&#xff0c;请大家多鼓励支持。 在现实生活中&#xff0c;很多人的资料是不愿意公布在互联网上的&#xff0c;但是我们又要使用人工智能的能力帮我们处理文件、做决策、执行命令那怎么办呢&#xff1f;于是我们构建自己或公司的私人GPT变得非常重要。 一、本地部署…...

EtherCAT主站SOEM -- 14 --Qt-Soem通过界面采集从站IO进行显示

EtherCAT主站SOEM -- 14 --Qt-Soem通过界面采集从站IO进行显示 一 mainwindow.c 文件函数:1.1 自定义PDO配置1.2 主站初始化二 motrorcontrol.c 文件三 allvalue.h 文件该文档修改记录:总结一 mainwindow.c 文件函数: 1.1 自定义PDO配置 int IO_setup(uint16 slave) {int...

线程安全、共享变量的可见性

Java中的线程安全问题 谈到线程安全问题&#xff0c;我们先说说什么是共享资源。所谓共享资源&#xff0c;就是说该资源被多个线程所持有或者说多个线程都可以去访问该资源。 线程安全问题是指当多个线程同时读写一个共享资源并且没有任何同步措施时&#xff0c;导致出现脏数…...

电动汽车BMS PCB制板的技术分析与可制造性设计

随着电动汽车行业的迅猛发展&#xff0c;各大厂商纷纷投入巨资进行技术研发和创新。电动汽车的核心之一在于其电池管理系统&#xff08;Battery Management System, BMS&#xff09;&#xff0c;而BMS的心脏则是其印刷电路板&#xff08;PCB&#xff09;。通过这篇文章探讨电动…...

Android 车联网——多屏多用户(十五)

前面几篇文章介绍了多用户和多屏相关的 Manager 和 Service。上一篇文章最后虽然车内乘员都根据配置有自己的对应屏幕,但默认情况下,所有车内乘员依然使用的是当前主用户(司机用户),这一篇我们继续放下看一下用户的创建与分配。 一、用户创建分配 1、创建用户 对于创建用…...

uwsgitop 使用

背景&#xff1a;Django项目 uwsgi&#xff0c;uwsgi.ini 在工程下。 使用&#xff1a; 下载安装uwsgitop [roothost ~]# tar -zxvf uwsgitop-0.11.tar.gz [rootuwsgitop-0.11 ~]# cd uwsgitop-0.11/ [rootuwsgitop-0.11 ~]# python setup.py install [rootuwsgitop-0.11 …...

深信服技术认证“SCSA-S”划重点:文件包含漏洞

为帮助大家更加系统化地学习网络安全知识&#xff0c;以及更高效地通过深信服安全服务认证工程师考核&#xff0c;深信服特别推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 *点击图片放大展示 深信服…...

Color Control

设计一个优秀的用户界面是一项艰巨的任务。特别是如果你想改变UI的颜色,调整所有元素可能需要花费大量时间。Color Control可以帮助你!在检查器中以可视化的方式将你的项目颜色定义为资源。Color Control为你提供了组件,当你编辑它们时,它们会自动更新你的UI元素。 颜色控制…...

端口开放问题

端口开放问题 所遇问题 在宿主主机上可以ping通虚拟机ip192.168.27.129&#xff0c;但无法在宿主主机上访问http://192.168.27.129:8080navavcat 16连接mysql时&#xff0c;2002 - Can’t connect to server on ‘192.168.27.129’(100601&#xff09; 原因 以上两个问题&a…...

KNN 回归

K 近邻回归&#xff08;K-Nearest Neighbors Regression&#xff09;是一种基于实例的回归算法&#xff0c;用于预测连续数值型的输出变量。它的基本思想是通过找到与给定测试样本最近的 K 个训练样本&#xff0c;并使用它们的输出值来预测测试样本的输出。它与 K 最近邻分类类…...

Kali Linux——获取root权限

目录 一、设置root密码 【操作命令】 【操作实例】 二、临时获取root权限 【操作命令】 【操作实例】 三、提升用户到root 1、获取root权限 2、进入/etc/passwd 3、查看root账号ID 4、找到需要修改的用户 5、输入i&#xff0c;进入编辑模式 6、把用户的ID改成跟r…...

听GPT 讲Rust源代码--compiler(28)

File: rust/compiler/rustc_codegen_llvm/src/llvm/mod.rs 文件rust/compiler/rustc_codegen_llvm/src/llvm/mod.rs是Rust编译器的LLVM代码生成模块的一个文件。该文件定义了一些用于与LLVM交互的结构体、枚举和常量。 此文件的主要作用是&#xff1a; 定义编译器和LLVM之间的接…...

Debezium日常分享系列之:Debezium2.5版本之connector for JDBC

Debezium日常分享系列之&#xff1a;Debezium2.5版本之connector for JDBC 一、概述二、JDBC 连接器的工作原理三、使用复杂的 Debezium 变更事件四、至少一次交付五、多项任务六、数据和列类型映射七、主键处理八、删除模式九、幂等写入十、Schema evolution十一、引用和区分大…...

爬虫网易易盾滑块案例:某乎

声明&#xff1a; 该文章为学习使用&#xff0c;严禁用于商业用途和非法用途&#xff0c;违者后果自负&#xff0c;由此产生的一切后果均与作者无关 一、滑块初步分析 js运行 atob(‘aHR0cHM6Ly93d3cuemhpaHUuY29tL3NpZ25pbg’) 拿到网址&#xff0c;浏览器打开网站&#xff0…...

织梦网站主页底/seo引擎

PTA习题11-7 奇数值结点链表 (20分) 题目如下&#xff1a; 本题要求实现两个函数&#xff0c;分别将读入的数据存储为单链表、将链表中奇数值的结点重新组成一个新的链表。链表结点定义如下&#xff1a; struct ListNode { int data; ListNode *next; };函数接口定义&#xff1…...

java 现代网站开发/网站推广途径和推广要点有哪些?

华夏之声-证券大本营链接:http://media.cnr.cn/来自 “ ITPUB博客 ” &#xff0c;链接&#xff1a;http://blog.itpub.net/39335/viewspace-350757/&#xff0c;如需转载&#xff0c;请注明出处&#xff0c;否则将追究法律责任。 转载于:http://blog.itpub.net/39335/viewspa…...

网站图片要多少像素/网站引流推广怎么做

我们今天来讲一个大部分竞赛生都最喜欢的一个数据结构——并查集 我们先从一道题入手&#xff1a;亲戚 这时并查集最经典的例题(注意&#xff01;没有之一&#xff01;) 我们来看一下这道题&#xff0c;大佬们肯定第一思路就是传递闭包(但是我认为没有没学过并查集的大佬) …...

个人求职网站履历怎么做/怎样搭建网站

alter system kill session&#xff0c;oracle杀死进程如果一个进程挂起或者占用太多资源拒绝其他会话&#xff0c;可以杀死这个进程。查询这个进程的数据库用户名&#xff0c;操作系统用户名&#xff0c;会话ID&#xff0c;会话序列号&#xff0c;和系统进程&#xff0c;找到s…...

ps做网站的优点/google网站推广

linux下如何查看邮件linux下如何查看邮件 我使用的是REDHAT9.0&#xff0c;当我登陆后&#xff0c;系统提示&#xff1a;YOU HAVE MAIL。 请问我改用什么命令查看邮件信息 mail命令即可...linux2013/03/11Linux下操作邮件的一些基础命令Linux下操作邮件的一些基础命令 系统提供…...

wordpress 代码转义/宁波seo外包费用

最近在用DevExpress Report做报表&#xff0c;根据一系列查询条件&#xff0c;然后Show报表。 今天在测试的过程中发现&#xff0c;如果资料量多的话&#xff0c;在报表呈现出来之前&#xff0c;画面上没有任何提示。这样对于使用者来说是不好的&#xff0c;应该提供一下报表显…...