CUDA编程之grid和block详解

CUDA


文章目录

  • CUDA
  • grid和block基本的理解
  • 1维 遍历
  • 2维 遍历
  • 3维 遍历
    • 3维 打印对应的thread


grid和block基本的理解

在这里插入图片描述

  1. Kernel:Kernel不是CPU,而是在GPU上运行的特殊函数。你可以把Kernel想象成GPU上并行执行的任务。当你从主机(CPU)调用Kernel时,它在GPU上启动,并在许多线程上并行运行。
  2. Grid: 当你启动Kernel时,你会定义一个网格(grid)。网格是一维、二维或三维的,代表了block的集合。
  3. Block: 每个block内部包含了许多线程。block也可以是一维、二维或三维的。
  4. Thread: 每个线程是Kernel的单个执行实例。在一个block中的所有线程可以共享一些资源,并能够相互通信。

grid、block和thread这些概念在硬件级别上并没有直接对应的实体,它们是抽象的概念,用于组织和管理GPU上的并行执行。然而,GPU硬件是专门设计来支持这种并行计算模型的,所以虽然线程在物理硬件上可能不是独立存在的,但是它们通过硬件架构和调度机制得到了有效的支持。

另外,对于线程的管理和调度,GPU硬件有特定的线程调度单元,如NVIDIA的warp概念。线程被组织成更小的集合,称为warps(在NVIDIA硬件上),并且这些warps被调度到硬件上以供执行。

所以,虽然这些概念是逻辑和抽象的,但它们与硬件的实际执行密切相关,并由硬件特性和架构直接支持。

一般来说:
• 一个kernel对应一个grid
• 一个grid可以有多个block,一维~三维
• 一个block可以有多个thread,一维~三维

1维 遍历

在这里插入图片描述

void print_one_dim(){int inputSize = 8;int blockDim = 4;int gridDim = inputSize / blockDim; // 2// 定义block和grid的维度dim3 block(blockDim);  // 说明一个block有多少个threadsdim3 grid(gridDim);    // 说明一个grid里面有多少个block /* 这里建议大家吧每一函数都试一遍*/print_idx_kernel<<<grid, block>>>();// print_dim_kernel<<<grid, block>>>();// print_thread_idx_per_block_kernel<<<grid, block>>>();// print_thread_idx_per_grid_kernel<<<grid, block>>>();cudaDeviceSynchronize();
}

我觉得重点在这两行

  1. dim3 block(blockDim);:
    这一行创建了一个三维向量block,用来定义每个block的大小。在这个例子中,blockDim是一个整数值4,所以每个block包含4个线程。dim3数据类型是CUDA中的一个特殊数据类型,用于表示三维向量。在这个情况下,你传递了一个整数值,所以block的其余维度将被默认设置为1。这意味着你将有一个包含4个线程的一维block。
  2. dim3 grid(gridDim);:
    这一行创建了一个三维向量grid,用来定义grid的大小。gridDim的计算基于输入大小(inputSize)和每个block的大小(blockDim)。在这个例子中,inputSize是8,blockDim是4,所以gridDim会是2。这意味着整个grid将包含2个block。与block一样,你传递了一个整数值给grid,所以其余维度将被默认设置为1,得到一个一维grid。

总体来说,这两行代码定义了内核的执行配置,将整个计算空间划分为2个block,每个block包含4个线程。你可以想象这个配置如下:

  • Block 0: 线程0, 线程1, 线程2, 线程3
  • Block 1: 线程4, 线程5, 线程6, 线程7

然后,当你调用内核时,这些线程将被用来执行你的代码。每个线程可以通过其线程索引和block索引来访问自己在整个grid中的唯一位置。这些索引用于确定每个线程应处理的数据部分。

block idx:   1, thread idx in block:   0, thread idx:   4
block idx:   1, thread idx in block:   1, thread idx:   5
block idx:   1, thread idx in block:   2, thread idx:   6
block idx:   1, thread idx in block:   3, thread idx:   7
block idx:   0, thread idx in block:   0, thread idx:   0
block idx:   0, thread idx in block:   1, thread idx:   1
block idx:   0, thread idx in block:   2, thread idx:   2
block idx:   0, thread idx in block:   3, thread idx:   3

2维 遍历

// 8个线程被分成了两个
void print_two_dim(){int inputWidth = 4;int blockDim = 2;  int gridDim = inputWidth / blockDim;dim3 block(blockDim, blockDim);dim3 grid(gridDim, gridDim);/* 这里建议大家吧每一函数都试一遍*/// print_idx_kernel<<<grid, block>>>();// print_dim_kernel<<<grid, block>>>();// print_thread_idx_per_block_kernel<<<grid, block>>>();print_thread_idx_per_grid_kernel<<<grid, block>>>();cudaDeviceSynchronize();
}
  • dim3 block(blockDim, blockDim);:
    这里创建了一个二维的block,每个维度的大小都是blockDim,在这个例子中是2。因此,每个block都是2x2的,包含4个线程。由于dim3定义了一个三维向量,没有指定的第三维度会默认为1。
  • dim3 grid(gridDim, gridDim);:
    同样,grid也被定义为二维的,每个维度的大小都是gridDim。由于inputWidth是4,并且blockDim是2,所以gridDim会是2。因此,整个grid是2x2的,包括4个block。第三维度同样默认为1。

因此,整个执行配置定义了2x2的grid,其中包括4个2x2的block,总共16个线程。你可以将整个grid可视化如下:

  • Block (0,0):
    线程(0,0), 线程(0,1)
    线程(1,0), 线程(1,1)
  • Block (0,1):
    线程(2,0), 线程(2,1)
    线程(3,0), 线程(3,1)
  • Block (1,0):
    线程(4,0), 线程(4,1)
    线程(5,0), 线程(5,1)
  • Block (1,1):
    线程(6,0), 线程(6,1)
    线程(7,0), 线程(7,1)

输出中的“block idx”是整个grid中block的线性索引,而“thread idx in block”是block内线程的线性索引。最后的“thread idx”是整个grid中线程的线性索引。

请注意,执行的顺序仍然是不确定的。你看到的输出顺序可能在不同的运行或不同的硬件上有所不同。

block idx:   3, thread idx in block:   0, thread idx:  12
block idx:   3, thread idx in block:   1, thread idx:  13
block idx:   3, thread idx in block:   2, thread idx:  14
block idx:   3, thread idx in block:   3, thread idx:  15
block idx:   2, thread idx in block:   0, thread idx:   8
block idx:   2, thread idx in block:   1, thread idx:   9
block idx:   2, thread idx in block:   2, thread idx:  10
block idx:   2, thread idx in block:   3, thread idx:  11
block idx:   1, thread idx in block:   0, thread idx:   4
block idx:   1, thread idx in block:   1, thread idx:   5
block idx:   1, thread idx in block:   2, thread idx:   6
block idx:   1, thread idx in block:   3, thread idx:   7
block idx:   0, thread idx in block:   0, thread idx:   0
block idx:   0, thread idx in block:   1, thread idx:   1
block idx:   0, thread idx in block:   2, thread idx:   2
block idx:   0, thread idx in block:   3, thread idx:   3

3维 遍历

dim3 block(3, 4, 2);
dim3 grid(2, 2, 2);

Block布局 (dim3 block(3, 4, 2)):

这定义了每个block的大小为3x4x2,所以每个block包含24个线程。
你可以将block视为三维数组,其中x方向有3个元素,y方向有4个元素,z方向有2个元素。
Grid布局 (dim3 grid(2, 2, 2)):

这定义了grid的大小为2x2x2,所以整个grid包含8个block。
你可以将grid视为三维数组,其中x方向有2个元素,y方向有2个元素,z方向有2个元素。
由于每个block包括24个线程,所以整个grid将包括192个线程。
整体布局可以视为8个3x4x2的block,排列为2x2x2的grid。

如果我们想用文字来表示整个结构,可能会是这样的:

  • Grid[0][0][0]:
    Block(3, 4, 2) – 24个线程
  • Grid[0][0][1]:
    Block(3, 4, 2) – 24个线程
  • Grid[0][1][0]:
    Block(3, 4, 2) – 24个线程
  • Grid[0][1][1]:
    Block(3, 4, 2) – 24个线程
  • Grid[1][0][0]:
    Block(3, 4, 2) – 24个线程
  • Grid[1][0][1]:
    Block(3, 4, 2) – 24个线程
  • Grid[1][1][0]:
    Block(3, 4, 2) – 24个线程
  • Grid[1][1][1]:
    Block(3, 4, 2) – 24个线程

这种三维结构允许在物理空间中进行非常自然的映射,尤其是当你的问题本身就具有三维的特性时。例如,在处理三维物理模拟或体素数据时,这种映射可能非常有用。

3维 打印对应的thread

在这里插入图片描述
比较推荐的打印方式

__global__ void print_cord_kernel(){int index = threadIdx.z * blockDim.x * blockDim.y + \threadIdx.y * blockDim.x + \threadIdx.x;int x  = blockIdx.x * blockDim.x + threadIdx.x;int y  = blockIdx.y * blockDim.y + threadIdx.y;printf("block idx: (%3d, %3d, %3d), thread idx: %3d, cord: (%3d, %3d)\n",blockIdx.z, blockIdx.y, blockIdx.x,index, x, y);
}

index是线程索引的问题,首先,考虑z维度。对于每一层z,都有blockDim.x * blockDim.y个线程。所以threadIdx.z乘以该数量给出了前面层中的线程总数,从图上看也就是越过了多少个方块

然后,考虑y维度。对于每一行y,都有blockDim.x个线程。所以threadIdx.y乘以该数量给出了当前层中前面行的线程数,也就是在当前方块的xy面我们走了几个y, 几行

最后加上thread x完成索引的坐标

void print_cord(){int inputWidth = 4;int blockDim = 2;int gridDim = inputWidth / blockDim;dim3 block(blockDim, blockDim);dim3 grid(gridDim, gridDim);print_cord_kernel<<<grid, block>>>();// print_thread_idx_per_grid_kernel<<<grid, block>>>();cudaDeviceSynchronize();
}
block idx: (  0,   1,   0), thread idx:   0, cord: (  0,   2)
block idx: (  0,   1,   0), thread idx:   1, cord: (  1,   2)
block idx: (  0,   1,   0), thread idx:   2, cord: (  0,   3)
block idx: (  0,   1,   0), thread idx:   3, cord: (  1,   3)
block idx: (  0,   1,   1), thread idx:   0, cord: (  2,   2)
block idx: (  0,   1,   1), thread idx:   1, cord: (  3,   2)
block idx: (  0,   1,   1), thread idx:   2, cord: (  2,   3)
block idx: (  0,   1,   1), thread idx:   3, cord: (  3,   3)
block idx: (  0,   0,   1), thread idx:   0, cord: (  2,   0)
block idx: (  0,   0,   1), thread idx:   1, cord: (  3,   0)
block idx: (  0,   0,   1), thread idx:   2, cord: (  2,   1)
block idx: (  0,   0,   1), thread idx:   3, cord: (  3,   1)
block idx: (  0,   0,   0), thread idx:   0, cord: (  0,   0)
block idx: (  0,   0,   0), thread idx:   1, cord: (  1,   0)
block idx: (  0,   0,   0), thread idx:   2, cord: (  0,   1)
block idx: (  0,   0,   0), thread idx:   3, cord: (  1,   1)

跟之前2D的一样, 同样看起来有点乱,是因为是异步执行的。

最后看一个多个grid的案例

void print_coordinates() {dim3 block(3, 4, 2);dim3 grid(2, 2, 2);print_cord_kernel<<<grid, block>>>();cudaDeviceSynchronize(); // 确保内核完成后才继续执行主机代码
}
block idx: (  0,   1,   0), thread idx:   0, cord: (  0,   4)
block idx: (  0,   1,   0), thread idx:   1, cord: (  1,   4)
block idx: (  0,   1,   0), thread idx:   2, cord: (  2,   4)
block idx: (  0,   1,   0), thread idx:   3, cord: (  0,   5)
block idx: (  0,   1,   0), thread idx:   4, cord: (  1,   5)
block idx: (  0,   1,   0), thread idx:   5, cord: (  2,   5)
block idx: (  0,   1,   0), thread idx:   6, cord: (  0,   6)
block idx: (  0,   1,   0), thread idx:   7, cord: (  1,   6)
block idx: (  0,   1,   0), thread idx:   8, cord: (  2,   6)
block idx: (  0,   1,   0), thread idx:   9, cord: (  0,   7)
block idx: (  0,   1,   0), thread idx:  10, cord: (  1,   7)
block idx: (  0,   1,   0), thread idx:  11, cord: (  2,   7)
block idx: (  0,   1,   0), thread idx:  12, cord: (  0,   4)
block idx: (  0,   1,   0), thread idx:  13, cord: (  1,   4)
block idx: (  0,   1,   0), thread idx:  14, cord: (  2,   4)
block idx: (  0,   1,   0), thread idx:  15, cord: (  0,   5)
block idx: (  0,   1,   0), thread idx:  16, cord: (  1,   5)
block idx: (  0,   1,   0), thread idx:  17, cord: (  2,   5)
block idx: (  0,   1,   0), thread idx:  18, cord: (  0,   6)
block idx: (  0,   1,   0), thread idx:  19, cord: (  1,   6)
block idx: (  0,   1,   0), thread idx:  20, cord: (  2,   6)
block idx: (  0,   1,   0), thread idx:  21, cord: (  0,   7)
block idx: (  0,   1,   0), thread idx:  22, cord: (  1,   7)
block idx: (  0,   1,   0), thread idx:  23, cord: (  2,   7)
block idx: (  1,   1,   1), thread idx:   0, cord: (  3,   4)
block idx: (  1,   1,   1), thread idx:   1, cord: (  4,   4)
block idx: (  1,   1,   1), thread idx:   2, cord: (  5,   4)
block idx: (  1,   1,   1), thread idx:   3, cord: (  3,   5)
block idx: (  1,   1,   1), thread idx:   4, cord: (  4,   5)
block idx: (  1,   1,   1), thread idx:   5, cord: (  5,   5)
block idx: (  1,   1,   1), thread idx:   6, cord: (  3,   6)
block idx: (  1,   1,   1), thread idx:   7, cord: (  4,   6)
block idx: (  1,   1,   1), thread idx:   8, cord: (  5,   6)
block idx: (  1,   1,   1), thread idx:   9, cord: (  3,   7)
block idx: (  1,   1,   1), thread idx:  10, cord: (  4,   7)
block idx: (  1,   1,   1), thread idx:  11, cord: (  5,   7)
block idx: (  1,   1,   1), thread idx:  12, cord: (  3,   4)
block idx: (  1,   1,   1), thread idx:  13, cord: (  4,   4)
block idx: (  1,   1,   1), thread idx:  14, cord: (  5,   4)
block idx: (  1,   1,   1), thread idx:  15, cord: (  3,   5)
block idx: (  1,   1,   1), thread idx:  16, cord: (  4,   5)
block idx: (  1,   1,   1), thread idx:  17, cord: (  5,   5)
block idx: (  1,   1,   1), thread idx:  18, cord: (  3,   6)
block idx: (  1,   1,   1), thread idx:  19, cord: (  4,   6)
block idx: (  1,   1,   1), thread idx:  20, cord: (  5,   6)
block idx: (  1,   1,   1), thread idx:  21, cord: (  3,   7)
block idx: (  1,   1,   1), thread idx:  22, cord: (  4,   7)
block idx: (  1,   1,   1), thread idx:  23, cord: (  5,   7)
block idx: (  0,   1,   1), thread idx:   0, cord: (  3,   4)
block idx: (  0,   1,   1), thread idx:   1, cord: (  4,   4)
block idx: (  0,   1,   1), thread idx:   2, cord: (  5,   4)
block idx: (  0,   1,   1), thread idx:   3, cord: (  3,   5)
block idx: (  0,   1,   1), thread idx:   4, cord: (  4,   5)
block idx: (  0,   1,   1), thread idx:   5, cord: (  5,   5)
block idx: (  0,   1,   1), thread idx:   6, cord: (  3,   6)
block idx: (  0,   1,   1), thread idx:   7, cord: (  4,   6)
block idx: (  0,   1,   1), thread idx:   8, cord: (  5,   6)
block idx: (  0,   1,   1), thread idx:   9, cord: (  3,   7)
block idx: (  0,   1,   1), thread idx:  10, cord: (  4,   7)
block idx: (  0,   1,   1), thread idx:  11, cord: (  5,   7)
block idx: (  0,   1,   1), thread idx:  12, cord: (  3,   4)
block idx: (  0,   1,   1), thread idx:  13, cord: (  4,   4)
block idx: (  0,   1,   1), thread idx:  14, cord: (  5,   4)
block idx: (  0,   1,   1), thread idx:  15, cord: (  3,   5)
block idx: (  0,   1,   1), thread idx:  16, cord: (  4,   5)
block idx: (  0,   1,   1), thread idx:  17, cord: (  5,   5)
block idx: (  0,   1,   1), thread idx:  18, cord: (  3,   6)
block idx: (  0,   1,   1), thread idx:  19, cord: (  4,   6)
block idx: (  0,   1,   1), thread idx:  20, cord: (  5,   6)
block idx: (  0,   1,   1), thread idx:  21, cord: (  3,   7)
block idx: (  0,   1,   1), thread idx:  22, cord: (  4,   7)
block idx: (  0,   1,   1), thread idx:  23, cord: (  5,   7)
block idx: (  1,   0,   0), thread idx:   0, cord: (  0,   0)
block idx: (  1,   0,   0), thread idx:   1, cord: (  1,   0)
block idx: (  1,   0,   0), thread idx:   2, cord: (  2,   0)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/51357.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用Chainlit接入通义千问快速实现一个多模态的对话应用

开通灵识服务 首先需要到阿里云-模型服务灵积开通账户&#xff0c;获得apiKey 模型服务灵积 https://dashscope.aliyun.com/ 进入控制台 &#xff0c;在API-KEY管理里&#xff0c;创建一个新的API-KEY,然后保存起来&#xff0c;后面会用到。 模型服务灵积服务所有API文档地址…

数据结构:构建数字世界的基石

在数字化时代&#xff0c;数据已成为推动社会进步与经济发展的核心驱动力。而数据结构&#xff0c;作为计算机科学的基础与核心&#xff0c;是存储、组织和管理这些海量数据的基石。无论是简单的应用程序还是复杂的系统架构&#xff0c;都离不开高效、灵活的数据结构支持。本文…

KUKA机器人外部自动IO信号经验分享

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

k8s学习--k8s集群部署kubesphere的详细过程

文章目录 kubesphere简介k8s群集部署kubespere环境准备工作一、配置nfs二、配置storageclass三、kubesphere开启服务(devops) kubesphere简介 下面是官方文档描述 KubeSphere 是在 Kubernetes 之上构建的面向云原生应用的分布式操作系统&#xff0c;完全开源&#xff0c;支持多…

【python】最新版抖音js逆向拿到数据,非常详细教程(附完整代码)

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…

如何压缩pdf文件大小?这5种方法,简单又实用!

如何压缩pdf文件大小&#xff1f;pdf是电脑常用的便携式文档格式&#xff0c;无论是在学术领域的论文发表&#xff0c;还是在商业场景中的合同签署&#xff0c;甚至是个人简历的提交&#xff0c;PDF文件都扮演着重要的角色。 电脑上存储pdf文件会面临一个难题&#xff0c;那就是…

收不到验证码邮件怎么办?快速排查与解决!

收不到验证码邮件有哪些应对策略&#xff1f;验证码邮件被拦截&#xff1f; 经常需要通过邮箱接收验证码邮件来完成各种操作。然而&#xff0c;许多人都会遇到收不到验证码邮件的情况。AokSend将详细介绍如何快速排查和解决收不到验证码邮件的问题。 收不到验证码邮件&#x…

Vue 项目部署后首页白屏问题排查与解决

引言 在部署 Vue.js 项目时&#xff0c;有时会遇到首页加载后出现白屏的情况&#xff0c;这可能是由于多种原因造成的。本文将介绍一些常见的排查方法和解决方案&#xff0c;帮助开发者快速定位问题并解决。 1. 常见原因分析 首页白屏的问题可能由以下几个方面的原因导致&am…

深入探索CSS3的Media Query:打造响应式网页设计的利器

在今天的互联网世界中&#xff0c;随着设备种类和屏幕尺寸的多样化&#xff0c;响应式网页设计&#xff08;Responsive Web Design, RWD&#xff09;已成为不可或缺的一部分。CSS3中的Media Query正是这一设计理念的实现利器&#xff0c;它允许开发者根据用户的设备特性和屏幕尺…

C++ 模板初级

引入 在讲函数模板之前就要先讲讲函数重载了 #include <iostream> using namespace std;int add(int a, int b) {return a b; }double add(double a, double b) {return a b; }int main() {cout << add(2, 3) << endl; cout << add(2.5, 3.7)…

pake 打包TodoList windows桌面应用exe

参考&#xff1a; rust安装 https://blog.csdn.net/weixin_42357472/article/details/125943880 pake网址 https://github.com/tw93/Pake 支持把网页打包成多终端应用 离线网站打包参考&#xff1a; https://github.com/tw93/Pake/wiki/Pake%E6%89%93%E5%8C%85%E9%9D%99%E6%8…

uniapp 小程序内嵌H5链接返回到小程序(亲测)

使用 weixin-js-sdk&#xff0c;特别注意的是&#xff0c;在vue项目中之前安装过 weixin-js-sdk 的&#xff0c;一定要要跟新&#xff0c;但是好像只可以更新到1.6.0版本&#xff0c;而微信要求最低版本是1.6.4&#xff0c;不过不影响使用。 安装weixin-js-sdk npm install w…

Unity Transform组件实现动画:基础与进阶技巧

在Unity中&#xff0c;Transform组件是控制游戏对象&#xff08;GameObject&#xff09;位置、旋转和缩放的核心组件。通过编程控制Transform组件&#xff0c;开发者可以创建各种动画效果。本文将介绍如何使用Transform组件实现动画&#xff0c;从基础的运动到更高级的动画技巧…

企业微信开发智能升级:AIGC技术赋能,打造高效沟通平台

文章目录 一、AIGC在企业微信开发中的核心价值1. 智能化客服体验2. 自动化工作流程3. 个性化内容推荐4. 深度数据分析与洞察 二、使用AIGC进行企业微信开发的实践路径1. 需求分析与场景定义2. 技术选型与平台搭建3. 模型训练与调优4. 接口对接与功能集成5. 测试与优化 《企业微…

Hugo 部署与自动更新(Git)

文章目录 Nginx部署Hugonginx.confhugo.conf Hugo自动更新Hugo自动更新流程添加访问令牌添加web hookrust实现自动更新接口 Nginx部署Hugo nginx.conf user nginx; worker_processes auto;error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid;even…

Lambda和Stream让代码简洁的七大原则

1. Lambda优于匿名内部类 函数接口&#xff1a;JDK 8中&#xff0c;只存在一个抽象方法的接口称为函数接口&#xff0c;使用FunctionalInterface注解标识。示例&#xff1a;FunctionalInterface public interface Comparator<T> {int compare(T o1, T o2); }Lambda表达式…

深入解读:几种常见视频大模型原理对比分析

在人工智能的快速发展中&#xff0c;视频分析技术逐渐成为研究的热点。伴随着深度学习的崛起&#xff0c;各种视频大模型应运而生&#xff0c;广泛应用于视频理解、动作识别、视频生成等领域。本文将对几种常见的视频大模型进行原理对比分析&#xff0c;帮助读者更好地理解它们…

JDK、JRE、JVM的区别

总结&#xff1a; JDK为开发人员提供了完整的开发和运行Java程序所需的工具和库&#xff0c;包括JRE、编译器&#xff08;javac&#xff09;、调试器&#xff08;jdb&#xff09;、Java类库&#xff08;如java标准库&#xff09;、基本开发工具&#xff1b; JRE为普通用户或需…

作业7.26~28

全双工&#xff1a; 通信双方 既可以发送&#xff0c;也可以接收数据 1. 利用多线程 或者 多进程&#xff0c; 实现TCP服务器 和 客户端的全双工通信 思路&#xff1a; 服务器和客户端&#xff0c; 在建立通信以后&#xff0c;可以创建线程&#xff0c;在线程编写另一个功能代…

实用网站推荐

​ 学习 前端 精简CSS格式 Font Awesome 图标库 BootCDN 加速服务 合集 AI工具集 动漫、音乐 娱乐 嗷呜动漫 奈飞同步 视频下载 B站视频解析下载 文件操作 ioDraw制作图 Convertio — 文件转换器 PDF处理 ​LOGO