开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

AI编码工具:JAVA朋友发的简单需求引起的国内AI血战 前

发表于 2024-04-22

前言

国内IT行业面临裁员压力,但AI技术的兴起为企业带来了转型机遇。AI可以提高生产效率,降低成本,并助力企业做出更精准的决策。特别是在编程领域,AI编码工具能够提升开发速度和代码质量,帮助程序员高效完成复杂任务。尽管AI可能替代某些岗位,但它也为技术人才创造了新的职业机会,要求他们不断学习新技能以适应变化。

哼哈:文章结尾有彩蛋!。。。ojbk!

需求背景

2024年4月22日清晨,滴答滴答滴哒哒哒。。。滴答滴答滴哒哒哒。。。滴答滴答滴哒哒哒。。。键盘声不断,勤劳的小蜜蜂开始上班啦。突然一位重量级9年JAVA资深大佬发了一段简单的需求给我简单探讨下需求是否清晰?基于平时有使用AI工具的习惯,于是乎用了几款AI工具做下效果测试。需求如下:

1
2
3
4
5
6
7
java复制代码@Override public String getSuppliercode(string orderNo, string productId, string channelId){ 
// 根据orderNo查询多个List结果
//如不存记录,suppliercode = orderNo
//如存在productId和channelId都相同的记录,则返回此suppliercode
//如不存在productId和channelId都相同的记录,则返回uuid
}
以上需求表对象为OrderDetail,请用Mybatis Plus补充以上解释代码,使用LambdaQueryWrapper查询数据,另外注意代码严谨需要预判OrderDetail获取属性时是否需要判空

国内AI PK对象

  1. 讯飞星火(科大讯飞):文本对话
  2. 通义灵码(阿里巴巴):文本对话,支持开发工具idea、vscode插件使用
  3. 文心一言(百度):文本对话
  4. CodeGeeX(智谱华章科技):文本对话,支持开发工具idea、vscode插件使用

AI编码实现效果对比

直接发送以上需求描述给AI生成代码,代码效果如下:

一、讯飞星火

效果描述:代码相对严谨,使用常规写法实现,但是没有提前判断传参是否为空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
java复制代码@Autowired  
private OrderDetailMapper orderDetailMapper;

@Override
public String getSuppliercode(String orderNo, String productId, String channelId) {
// 根据orderNo查询多个List结果
LambdaQueryWrapper<OrderDetail> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrderDetail::getOrderNo, orderNo);
List<OrderDetail> orderDetailList = orderDetailMapper.selectList(queryWrapper);

// 如果不存在记录,返回orderNo作为suppliercode
if (orderDetailList.isEmpty()) {
return orderNo;
}

// 遍历查询结果,查找是否存在productId和channelId都相同的记录
for (OrderDetail orderDetail : orderDetailList) {
if (orderDetail.getProductId() != null && orderDetail.getChannelId() != null) {
if (orderDetail.getProductId().equals(productId) && orderDetail.getChannelId().equals(channelId)) {
return orderDetail.getSuppliercode();
}
}
}

// 如果不存在productId和channelId都相同的记录,则返回uuid
return UUID.randomUUID().toString();
}

二、通义灵码

效果描述:代码相对严谨,使用常规写法实现,且有提前判断传参是否为空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
java复制代码@Autowired  
private OrderDetailMapper orderDetailMapper;

@Override
public String getSuppliercode(String orderNo, String productId, String channelId) {

// 预判输入参数是否为空
if (StringUtils.isAnyBlank(orderNo, productId, channelId)) {
throw new IllegalArgumentException("orderNo, productId, and channelId must not be blank");
}

// 根据orderNo查询多个OrderDetail记录
LambdaQueryWrapper<OrderDetail> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrderDetail::getOrderNo, orderNo);

List<OrderDetail> orderDetailList = orderDetailMapper.selectList(queryWrapper);

// 处理查询结果
if (orderDetailList.isEmpty()) {
return orderNo; // 如不存在记录,返回orderNo作为suppliercode
} else {
for (OrderDetail orderDetail : orderDetailList) {
if (StringUtils.equals(orderDetail.getProductId(), productId)
&& StringUtils.equals(orderDetail.getChannelId(), channelId)) {
return orderDetail.getSuppliercode(); // 如存在productId和channelId都相同的记录,返回此suppliercode
}
}
}

// 如不存在productId和channelId都相同的记录,生成并返回UUID作为suppliercode
return UUID.randomUUID().toString();
}

三、文心一言

效果描述:

  1. 代码相对严谨,使用常规写法实现,且使用JDK1.8 Stream API写法;
  2. 存在部分代码不严谨:没有提前判断传参是否为空;
  3. 存在需求理解误差1:orderDetail.getProductId().equals(productId) && orderDetail.getChannelId().equals(channelId)前置参数没有判空,可能导致空指针异常;
  4. 存在需求理解误差2:
    以下原需求是直接返回匹配成功的matchedOrderDetail.getSuppliercode(),预判suppliercode属性是否需要判空则指的是3对应空指针问题。

// 预判suppliercode属性是否需要判空,如果suppliercode字段在数据库中允许为null,则需要进行判空 return matchedOrderDetail.getSuppliercode() != null ? matchedOrderDetail.getSuppliercode() : orderNo; // 或者选择其他默认值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
java复制代码
@Autowired
private OrderDetailMapper orderDetailMapper;

public String getSuppliercode(String orderNo, String productId, String channelId) {
// 创建LambdaQueryWrapper进行条件查询
LambdaQueryWrapper<OrderDetail> queryWrapper = Wrappers.<OrderDetail>lambdaQuery()
.eq(OrderDetail::getOrderNo, orderNo); // 根据orderNo查询

// 查询满足条件的所有记录
List<OrderDetail> orderDetailList = orderDetailMapper.selectList(queryWrapper);

// 如果没有记录,返回orderNo作为suppliercode
if (orderDetailList == null || orderDetailList.isEmpty()) {
return orderNo;
}

// 尝试找到productId和channelId都相同的记录
Optional<OrderDetail> optionalOrderDetail = orderDetailList.stream()
.filter(orderDetail -> orderDetail.getProductId().equals(productId) && orderDetail.getChannelId().equals(channelId))
.findFirst();

// 如果找到了符合条件的记录,返回其suppliercode
if (optionalOrderDetail.isPresent()) {
OrderDetail matchedOrderDetail = optionalOrderDetail.get();
// 预判suppliercode属性是否需要判空,如果suppliercode字段在数据库中允许为null,则需要进行判空
return matchedOrderDetail.getSuppliercode() != null ? matchedOrderDetail.getSuppliercode() : orderNo; // 或者选择其他默认值
}

// 如果没有找到符合条件的记录,生成UUID并返回
return UUID.randomUUID().toString();
}

四、CodeGeeX

效果描述:

  1. 代码不够严谨,有明显bug;
  2. bug1:查询时多带了productId、channelId作为查询条件;
  3. bug2:getList(queryWrapper)没有输出这个方法的实现;
  4. bug3:如存在productId和channelId都相同的记录,则返回此suppliercode,判断条件不准确;
  5. 访问多次后出现网络中断情况,体验感不佳。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
java复制代码@Override  
public String getSuppliercode(String orderNo, String warehouseld, String binld) {
// 根据orderNo查询多个List结果
LambdaQueryWrapper<OrderDetail> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrderDetail::getOrderNo, orderNo);
queryWrapper.eq(OrderDetail::getProductId, productId);
queryWrapper.eq(OrderDetail::getChannelId, channelId);
List<OrderDetail> orderDetailList = getList(queryWrapper);

// 如不存在记录,suppliercode=orderNo
if (orderDetailList.isEmpty()) {
return orderNo;
}

// 如存在productId和channelId都相同的记录,则返回此suppliercode
for (OrderDetail orderDetail : orderDetailList) {
if (orderDetail.getProductId() != null && orderDetail.getChannelId() != null) {
return orderDetail.getSuppliercode();
}
}

// 如不存在warehouselD和binld都相同的记录,则返回uuid
return UUID.randomUUID().toString();
}

效果总结:

本人目前使用通义灵码+讯飞星火多一些

  1. 通义灵码:效果最佳,需求理解到位,且代码有做前置校验,较为严谨;
  2. 讯飞星火:效果较好,需求理解到位,整体较为不错;
  3. 文心一言:效果一般,需求理解有误差;
  4. CodeGeeX:效果较差,需求理解有误差,且有明显bug,代码无法使用,且使用调用失败率较高。

补充:

  1. 以上测试的都是本人经常使用的AI工具,国内的AI工具能力及准确性良莠不齐,需要靠自己去不断测试摸索;
  2. AI工具使用效果除了跟AI工具自身模型算法能力有关,还与需求者的需求描述是否精准有关,日常工作中我们需要提高自身语言描述能力及需求输出能力,以适应以后AI盛行的市场环境,提高效率及实现创意思路;
  3. 国外penAI GPT、New Bing、GitHub Copilot、Google Colab Copilot等AI工具能力也特别强,有兴趣可以多测试、多学习。

彩蛋:

当当。。当当。。。彩蛋来啦,AI工具集在此,赶紧收藏。

biu...biu...biu...biu,觉得文章对您有帮助的话一键三连,点赞点拨关注点评一下呗!^_^

AI工具集

image.png

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

都2024年了,你还不知道git worktree么? 应用

发表于 2024-04-22

三年前 python 大佬吉多·范罗苏姆(为 Python 程序设计语言的最初设计者及主要架构师)才知道 git worktree ,我现在才知道,我觉得没啥丢人的。

应用场景

如果你正在 feature 的分支中开发新功能,线上版本紧急错误又需要你基于 master 做修复。

可能有如下几种办法解决:

解法 1

  1. 将本地修改文件通过 git add . && git commit 提交到当前分支,或者通过 git stash 暂存起来
1
2
3
4
5
6
7
8
git复制代码// feature branch
git add .
git commit
git checkout master

// or
git stash
git checkout master
  1. 分支切到 master,修复完问题后切回 feature
  2. 若之前是 commit 的可直接开发,若是 git stash 则 git stash pop 出来处理

解法 2

clone 一份同样远程代码到本地,切换到 master 分支来解决错误。

存在问题

解法 1 缺点:

  1. 若正在跑一个耗时较长的测试不能动工作区代码,则只能等了
  2. 用 git stash 方式,暂存和切走到修改完错误切回 feature 至少得 4 步,命令行切来切去也挺麻烦

解法 2 缺点:若远程仓库太大,则 clone 一次会很耗时。

针对上述场景,可以考虑用 git worktree 来解决,该命令可让你直接开一个独立工作区域出去做别的事,未 commit 的档案原封不动留在原来分支。

它如何工作

  1. 首先创建一个本地文件夹 worktree_test 并在 worktree_test 文件夹内创建 master 分支,然后在 master 内 git init;
1
shell复制代码mkdir worktree_test && cd worktree_test && mkdir master && cd master/ && git init
  1. 先模拟构造一个主干分支 master,向 rooi.txt 文件中写几行内容#1
1
2
3
4
5
6
7
shell复制代码//master
vi rooi.txt
cat rooi.txt
master:add:rooi.txt (#1)

git add rooi.txt
git commit -m "add:rooi.txt"
  1. 构造完主干分支 master 后,我们生成一个 feature 分支,该分支模拟开发功能分支,并写一些分支独有内容#2:feature:modify rooi.txt 和#4 生成一个临时 untracked file: a.temp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
shell复制代码// new branch in master
git checkout -b feature

// feature
cat rooi.txt
master:add:rooi.txt (#1)

vi rooi.txt (#2)
cat rooi.txt
master:add:rooi.txt
feature:modify rooi.txt

git add rooi.txt (#3)
git commit -m "dev:modify"
[feature 4d300fb] dev:modify
1 file changed, 1 insertion(+)

touch a.temp (#4)

git status (#5)
Untracked files:
a.temp

线上紧急错误来了

此时我们在 feature 分支通过 git worktree add 建立一个工作区#1, 注意此时临时目录区在 d:\work\worktree_test 目录下
说明: git worktree add 中 ../bugfix 表示新建工作区名字,后面 master 表示跟进当前主干 master 分支来新建,也就是当前工作区 bugfix 是完全以 master 为基准建立的副本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
shell复制代码// d:\work\worktree_test\master  in feature
git worktree add ../bugfix master (#1)
// to dir : d:\work\worktree_test
cd ..

// d:\work\worktree_test
ls
bugfix/ master/

cd bugfix
cat rooi.txt
master:add:rooi.txt

vi rooi.txt
cat rooi.txt
master:add:rooi.txt
bugfix:fix bug

git add rooi.txt (#3)
git commit -m "bugfix:fix bug"

如上,bugfix 分支是依照 master 分支构建工作区,此时可以直接提交到 master 分支上。

继续开发功能

通过#1 回到原来 master 目录下可以看到,之前提交和 untracked file 都存在,且在 feature 分支上,现场原样保持。

1
2
3
4
shell复制代码// in bugfix dir master
// d:\work\worktree_test\bugfix
cd ..
cd master (#1)

当然你也可以合并 bugfix 到 feature 分支

1
2
shell复制代码// feature
git merge master

如下效果图中,可以创建多个工作区副本

参考资料

It’s 2024, I Haven’t Used Git Worktree Yet | by David Lee | Mar, 2024 | Medium

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

拿到一台新MAC,如何快速配置并运行Flutter项目

发表于 2024-04-22

前言

大家好,我是未央歌,一个默默无闻的移动开发搬砖者~

最近我的电脑因为升级要求需要格式化,悲催,一切要重新开始,本文就记录了我的配置之路,可谓保姆级讲解记录,方便后面有需要回顾查看,如果你也有需要,不妨收藏看看是否用得上。

必备软件安装

Android Studio

  • 无需多言,下载 AS 或者 VSCode,全凭个人喜好习惯

Sidekick

  • Flutter SDK 管理工具
  • 安装完成后,选择一个你需要的版本下载,并设置为全局。
    image.png

环境配置

查看电脑使用的shell是Bash还是Zsh

  • 在终端输入 echo $SHELL ,该命令将显示当前默认 shell 的路径
  • 如果是 Bash,你将看到 /bin/bash,如果是 Zsh,你将看到 /bin/zsh。
  • 以我的 MAC 为例,是 /bin/bash

检查 Homebrew 是否安装

  • 在终端输入 brew doctor ,如果出现 brew: command not found,则说明电脑还没安装 Homebrew
  • 输入 以下命令安装
1
bash复制代码/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  • 安装完成后,再次执行 brew doctor ,如果出现 your system is ready to brew,则表示 Homebrew 已经安装成功
  • 如若仍然出现 brew: command not found,在终端输入以下命令更新路径配置
1
2
bash复制代码echo 'export PATH="/usr/local/bin:$PATH"' >> ~/.bash_profile(如果使用 bash shell)
echo 'export PATH="/usr/local/bin:$PATH"' >> ~/.zshrc(如果使用 zsh shell)
  • 安装完提示如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
bash复制代码...
Installation successful!

==> Homebrew has enabled anonymous aggregate formulae and cask analytics.
Read the analytics documentation (and how to opt-out) here:
https://docs.brew.sh/Analytics
No analytics data has been sent yet (nor will any be during this install run).

==> Homebrew is run entirely by unpaid volunteers. Please consider donating:
https://github.com/Homebrew/brew#donations

==> Next steps:
- Run these two commands in your terminal to add Homebrew to your PATH:
(echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> /Users/10583/.bash_profile
eval "$(/opt/homebrew/bin/brew shellenv)"
- Run brew help to get started
- Further documentation:
https://docs.brew.sh
...
  • 此时再执行以下命令,按回车即可完成
1
2
bash复制代码(echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> /Users/10583/.bash_profile
eval "$(/opt/homebrew/bin/brew shellenv)"
  • 然后重启终端再次执行 brew doctor

Homebrew 问题一览

  • 升级卡住,果出现以下这种情况,一直卡在 Cloning into……
    image.png
  • 输入以下命令
1
2
3
4
5
6
bash复制代码cd /usr/local/Homebrew/Library/Taps/
mkdir homebrew
cd homebrew
git clone https://mirrors.ustc.edu.cn/homebrew-core.git
或 git clone https://mirrors.ustc.edu.cn/homebrew-cask.git (哪个卡住 Clone 哪个)
brew update
  • 如果brew提示 formula.jws.json 文件没有
    image.png
  • 在环境配置文件中添加 export HOMEBREW_NO_INSTALL_FROM_API=1 即可,具体添加方式见下方更改环境配置

查vim是否安装

  • 输入 vim ~/.bash_profile,如果出现 vim: command not found ,说明没安装 vim
  • 输入 brew install vim 进行安装

更改环境配置

  • 输入 vim ~/.bash_profile,打开环境变量文件
  • 按 i 键,即可写入下方的配置
1
2
3
4
5
6
bash复制代码# Flutter
# Flutter SDK位置
export PATH=$HOME/fvm/default/bin:$PATH
# Flutter 网络代理
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
  • 写入完成后,按 esc 退出编辑
  • 输入 :wq 保存并关闭。(若此时遇到 readonly option is set (add!to override),执行 :wq! 强制保存退出)
  • 执行 source ~/.bash_profile 让前面编辑的 vim ~/.bash_profile 文件生效
  • 运行 flutter –version,看是否生效

安装cocoapods

  • 执行 brew install cocoapods
  • 验证 cocoapods 是否已成功安装,执行 pod –version

运行至模拟器

创建 iOS 模拟器

  • 在 App Store 上搜索 Xcode 并安装。
  • 安装完打开 Xcode,选择菜单栏的 window,选择 Devices and Simulators
    image.png
  • 然后选择 Platforms,去下载 iOS 相关资源(图为已下载)
    image.png
  • 然后在 Xcode 打开模拟器
    image.png
  • 选择其中一个型号启动即可
    image.png

运行 Flutter 项目至 iOS 模拟器

  • 如果运行起来报错 increase your application’s deployment target to at least 13.0 as described at docs.flutter.dev/deployment/…
    image.png
  • 去到项目中双击打开 Runner.xcworkspace
    image.png
  • 在如下路径中将 iOS Deployment Target 改为 13.0
    image.png
  • 然后打开项目中 Podfile 文件,将 platform 也改成13.0
  • 最后 cd 进入到这个 iOS 目录下,执行 pod install 命令
  • 最后如何其他问题,一般都可以运行啦,如果遇到其他问题,欢迎评论区提出,一起解决~

最后

如果你对 Android 和 Flutter 感兴趣,可以订阅我的专栏:

  • Android 知识
  • Flutter
  • Flutter问题收集及解决

感谢大家的支持,码字实在不易,其中如若有错误,望指出,记得点赞关注加收藏哦 ~

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Linux之父讽刺AI炒作:很搞笑,大概我也会被大模型取代

发表于 2024-04-22

Linus Torvalds 对硬件错误、「邪恶的」开发者和搞笑的人工智能炒作发表了自己的看法。

几天前,由 Linux 基金会主办的北美开源峰会(Open Source Summit North America)在华盛顿西雅图闭幕。

会上,Linux 之父 Linus Torvalds 与其好友、Verizon 开源项目办公室负责人 Dirk Hohndel 展开了一场对话,深入探讨了 Linux 开发及相关问题。外媒 ZDNet 对他们两人的对话进行了整理。

图片

左为 Linus Torvalds、右为 Dirk Hohndel。

首先谈到的话题是源代码中的制表符与空格。此前一位开发者建议用用空格替换制表符,以帮助 Kconfig 解析器读取文件。

不过,Torvalds 认为这是一个糟糕的举动。

在最近发布的 Linux 6.9-rc4 版本中,Torvalds 采取了特别措施来应对那些无法正确处理制表符的 Kconfig 解析器。他故意地在通用 Kconfig 文件中加入了一些制表符。

图片

图源:git.kernel.org/pub/scm/lin…

谈到即将发布的 Linux 6.9 内核版本,Torvalds 形容它「平静、稳定且无聊」。或许对于一个有着 30 多年历史的软件项目来说,本该如此。如果每个版本都有大的改变,可能意味着出现了问题。

他们随后谈到硬件问题。如果硬件错误持续存在的话,可能会导致安全问题。

对此,Torvalds 表示,这令人沮丧。开发者通常可以快速地修复软件问题,但硬件由于迭代无法及时修复。

此外随着 RISC-V 等开放硬件的兴起,这些问题或许不会困扰下一代硬件。但是 Torvalds 觉得事实并不是如此。

他认为,RISC-V 及开发者会犯其他人犯过的所有错误。Torvalds 解释称,硬件人员与软件人员不同,他们之间存在着相当大的鸿沟。并且,硬件开发者重新发明了旧的做事方法,只能通过犯以前犯过的所有相同错误来学习。这令人悲伤,但却是事实。

不过,Torvalds 预计事情会进展得更快。Hohndel 也指出,Linux 在消除用户硬件平台之间的差异方面做得越来越好。「10 年前,从 x86 迁移到不同的平台仍然非常困难。如今,大多数人甚至都不知道自己运行的是 AMD 还是英特尔芯片。都在云端,一切看起来一模一样。」

接着他们谈到,一些开源项目最近遇到的问题是:有些邪恶的开发者看起来人畜无害、乐于助人,实际上却是有恶意的。比如,微小的 Linux XZ Util 程序内有一个安全后门,它非常容易传播到主流 Linux 发行版中。

这些问题被及时制止了,本身也不是 Linux 的问题,但还是令人担忧。Torvalds 回忆到,2021 年,有人尝试将不良补丁推送到 Linux 中,但他们失败了。Linux 维护者发现了这些不良补丁,但为此感到非常沮丧和生气。

Linux 社区是独一无二的,它拥有 1000 多名开发者,其中很多人已经合作了数十年。因此,黑客试图通过欺骗手段将不良代码植入到 Linux 内核是不可能完成的任务,但其他大多数程序就没这么幸运了。

图片

即便如此,Torvalds 指出,当居心不良者利用成为维护者的机会来实施不良行为时,几周之内就会被发现。开源项目发现这类攻击,意味着事情会得到解决。因此,一个健康的社区构成了最好的防御。

当然,这并不适用 99% 的开源项目,这些项目往往很小。我们需要保持警惕,并知道自己可以信任谁。在 Linux 内核中,我们将 PGP (Pretty Good Privacy) 作为信任网络的基础。

Torvalds:AI 并不能解决一切问题

与此同时,Torvalds 认为不要期待人工智能会成为最后的那个答案。

这两位业界开源领袖仍对人工智能炒作持疑。Torvalds 调侃道:「这看起来很搞笑,我也可能会被人工智能模型取代。」Hohndel 则认为,当今大多数人工智能就像是增强版的自动更正。

Torvalds 随即将他的态度总结为:「让我们等上 10 年,看看它实际上会发展到哪一步,我们再来作出所有这些令世人疯狂的声明。」

这并不是意味着两位领袖对人工智能在未来的有益性持悲观态度。

事实上,Torvalds 指出了一个人工智能的良好作用:英伟达已经开始更好地与 Linux 内核开发团队进行持续的沟通,并与 Linux 内存管理部门开展了良好的合作,从而在 Linux 上更加高效地运行人工智能大语言模型(LLMs)。

Torvlads 也表示:「我们对现在所持有的较多工具能够切实寻找到问题点、漏洞报以较大期许,并持有经久不变的信心,但使这些工具变得更加智能化并不是一件坏事。使用智能化工具只是面对时代变化的一个不可避免的步骤。我们有一些通过高度复杂的脚本及模式识别功能,能够对内核进行重写的工具。由于我们必须在最低满足点指定事物,所以导致该类工具变得难以使用,但人工智能会在这一关键阶段起到非常大的正向作用。」

此外,在谈到 AI BS 时,Torvalds 表示必须要变得警惕。Honhndel 随即开玩笑道:「他是在指美妙的科学。美妙的科学在这个时代进来又出去。」

对于是否会继 Linux 和 Git 之后继续开发新的大项目时,Torvalds 则当即表示:希望不会如此。

他的解释是:我希望这一切不会发生,因为我开启每一个项目都是源于我对他人变得无能或贪恋而感到深深的沮丧。比如我开发 Linux 的原因是因为我已经无法支付真正的东西。这一切相当困难,33 年后,我仍然为此工作。

Torvalds 也提到自己 20 年前犯了同样的错误。当时他说自己实在不敢苟同源代码管理(SCM)是极其有趣的事情,但在我之前的所有人 - 他们显然都完全搞错了。所以我需要自己去做,这有多难呢?

因此,Torvalds 希望再也不会遇到这种情况了。如今,Torvalds 仍坚守在 Linux 和 Git 的工作岗位上,因为「当其他人过来围在我身边并跟我说『我们真的需要你』时,我十分确信,没有他们,我就不会继续坚守这一切。虽然我的产品源于我需要的东西,但真正使它们不断前进的原因是它们确实对另一些人产生了真正有意义的作用。」

Torvalds 最后表示,只要我们发现 Linux 切实发挥了作用,自己和团队将会继续不懈地努力改进它。

原文链接

www.zdnet.com/article/the…

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

独立开发(裸辞)100天,我的阶段性复盘

发表于 2024-04-22

嗨,大家好,我是徐小夕。从裸辞到现在已经快100天了,今天做一个系统性的复盘,聊聊这100天,我都做了些什么。

从零代码和可视化说起

4年前一个夜高风黑的夜晚,我决定做一款前端效率工具,通过拖拽式来帮助技术人员快速搭建页面,也就是现在的 H5-Dooring 零代码平台。

当时在github开源后, 迅速得到了很多小伙伴的支持和反馈,也给了我持续迭代的决心,从开源到github H5搭建领域 top1,我花了一年时间,后面由于时间和成本问题, 我开始了做开源商业化的尝试。

断断续续花了两年的时间打磨产品, 团队建设,以及和企业客户的沟通,技术咨询,逐渐跑通了一个商业模式——企业私有化部署, 俗称软件授权。

后面和小伙伴也慢慢讨论了一些其他的商业模式,比如saas,可视化解决方案定制开发等,也取得了一些成绩:

  • saas注册用户1年半时间突破1万+
  • 平台累计产生页面数1.1万+
  • 累计访问量(pv)突破5000万

当然在可视化解决方案上,也积累了大量的经验,为后续技术设计和研发带来了很多帮助。

界面也从程序员的审美,慢慢升级到从产品经理的角度思考整个产品体验:

大家感兴趣也可以参考一下H5-Dooring的产品设计:

github地址: H5-Dooring零代码搭建平台

陆陆续续的技术分享

作为一名对技术有追求的前端,我平时也非常热爱技术分享,这几年陆陆续续在掘金,知乎, 我的个人公众号《趣谈前端》等技术平台分享自己的可视化实现方案和产品技术思路,也参加过很多线下的技术大会作为技术嘉宾进行过分享,这个过程非常锻炼,也很折磨作为技术出身的程序员品种。

如果大家对低代码,零代码或者可视化感兴趣,也可以参考一下我之前的技术分享文章。

辞职的这100天里,我也在抽时间学习新的技术,比如 nextjs,vite等,在研究学习期间,基于自己的想法,又用 vue3 开发了一款表单搭建平台, 橙子试卷:

感觉实战经验慢慢拉满, 表单引擎上线之后2天内积累了200+使用用户,还是有点小成就的~

继续开源之路

辞职之后时间变多了,也有更多的时间做开源, 也陆陆续续迭代了很多开源项目,比如图片编辑器,开箱即用的nextjs的管理后台,nodejs解决方案等,

  • 徐小夕的github主页

如果大家感兴趣也可以在我 github 主页看到我的开源项目,学习参考。

研究AI + 零代码结合

最近两年AI发展的很迅猛,很多软件都在使用ai或者开发AI模型,所以我和小伙伴也略学习了一下,把AI能力做了一些和低代码的结合, 实现方案基本都跑通了,比如:

  • AI生成零代码的文案和图片素材
  • 通过一段话, 快速生成H5网页
  • 通过AI来自动生成零代码的组件

当然还有更多深入的结合,后续我会持续和大家分享。接下来展示一下在Doroing里使用aigc的能力:

如果你想生成一个专业的网站, 使用Dooring + AI的能r力也许能帮助到你。当然我们还在收集大家的建议, 如果有好的建议也随时和我反馈。

关于团队

可持续的产品也离不开一个高执行力的团队,我也找到了很多志同道合的小伙伴一起做能改变世界一点点的事情, 也欢迎感兴趣的小伙伴可以加入一起共建。

关于辞职后的收入问题

毕竟作为一个技术博主,还是有一些赚钱路子的, 比如产品授权,技术咨询,广告收入等,月入5位数不是太大的问题。我圈子了也有很多自由职业的小伙伴,也非常值得学习。

自由职业的成本

当然辞职之前一直做前端架构师或者前端负责人的岗位,所以基本1-2年就能赚到100w+的收入, 但是奈何自己对技术的追求,想有更多的专研时间和探索期,所以综合评估了,最后还是觉得对技术和产品有更多的深造时间。

也许未来的某一天,我又踏上了职场卷王之路,我给自己的时间是6个月,回想一下也只有2个月时间了,加油吧,为梦想和技术追求!

对了,最后

最近开发了一款文档编辑器,类似飞书,钉钉文档,后面打算做成能小规模替代飞书文档的产品,欢迎体验反馈~

Nocode/doc文档体验入口

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

MyDumper “喜欢” 触发器么? 介绍 实验室 题外话

发表于 2024-04-22

是的,但现在它更“喜欢”它们,原因如下。

file

介绍

使用 LIKE 子句过滤特定表中的触发器或视图很常见。但是,它可能会欺骗您,特别是如果您看不到输出(即在非交互式会话中)。让我们看一个简单的例子,以及如何以更可靠的方式处理任务。还有一个指向 mydumper 错误的额外链接,该错误是根据本实验室的调查而修复的。是的,但现在它更喜欢它们,原因如下。

实验室

首先,我们将创建两个只有一个无符号整数列的虚拟表。该列也将是两个表的主键。

1
2
3
4
5
6
7
8
9
sql复制代码CREATE TABLE `test_lab` (
`id` int unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

CREATE TABLE `test2lab` (
`id` int unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

我们还将创建第三个表,它将作为我们的“日志”来跟踪前两个表中所做的插入。我们将在这里使用复合主键(表名称和 ID)。

1
2
3
4
5
sql复制代码CREATE TABLE `log_lab` (
`changed_table` varchar(50) NOT NULL,
`id` int unsigned NOT NULL,
PRIMARY KEY (`changed_table`,`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

现在,让我们为每个表添加一个触发器。两个表的触发器相同,并将记录复制到日志表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sql复制代码DELIMITER //

CREATE TRIGGER `test_lab_trigger_INS` AFTER INSERT ON `test_lab`

FOR EACH ROW BEGIN
INSERT INTO log_lab (`changed_table`,`id`) VALUES ('test_lab', NEW.id) ;
END
//

CREATE TRIGGER `test2lab_trigger_INS` AFTER INSERT ON `test2lab`

FOR EACH ROW BEGIN
INSERT INTO log_lab (`changed_table`,`id`) VALUES ('test2lab', NEW.id) ;
END
//
DELIMITER ;

最后,让我们检查一下我们是否正确完成了所有操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sql复制代码mysql> insert into `test_lab` values (8) ;
Query OK, 1 row affected (0.00 sec)

mysql> insert into `test2lab` values (2) ;
Query OK, 1 row affected (0.01 sec)

mysql> select * from log_lab ;
+---------------+----+
| changed_table | id |
+---------------+----+
| test_lab | 8 |
| test2lab | 2 |
+---------------+----+
2 rows in set (0.00 sec)

现在,我们已准备好运行我们为其创建实验室的测试用例。让我们从使用这个语句开始:

1
2
3
4
5
6
7
8
9
sql复制代码mysql> SHOW TRIGGERS LIKE 'test2lab';
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
| Trigger | Event | Table | Statement | Timing | Created | sql_mode | Definer | character_set_client | collation_connection | Database Collation |
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
| test2lab_trigger_INS | INSERT | test2lab | BEGIN
INSERT INTO log_lab (`changed_table`,`id`) VALUES ('test2lab', NEW.id) ;
END | AFTER | 2024-04-13 10:35:45.29 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION | msandbox@localhost | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci |
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
1 row in set (0.00 sec)

一张表,一个触发器,一行。都可以。

这次,我们将使用其他表名称:

1
2
3
4
5
6
7
8
9
10
11
12
sql复制代码mysql> SHOW TRIGGERS LIKE 'test_lab' ;
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
| Trigger | Event | Table | Statement | Timing | Created | sql_mode | Definer | character_set_client | collation_connection | Database Collation |
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
| test2lab_trigger_INS | INSERT | test2lab | BEGIN
INSERT INTO log_lab (`changed_table`,`id`) VALUES ('test2lab', NEW.id) ;
END | AFTER | 2024-04-13 10:35:45.29 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION | msandbox@localhost | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci |
| test_lab_trigger_INS | INSERT | test_lab | BEGIN
INSERT INTO log_lab (`changed_table`,`id`) VALUES ('test_lab', NEW.id) ;
END | AFTER | 2024-04-13 10:35:45.29 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION | msandbox@localhost | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci |
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
2 rows in set (0.00 sec)

人们可能会惊讶地看到两个表都有两行及其触发器,但这是预期的行为。 LIKE 语句不会根据模式执行严格的比较和过滤。您仍然可以在其中使用通配符 (%),它可以用来代替搜索字符串中任意数量的字符。在 LIKE 语句中使用占位符 (_) 来匹配任何单个字符的情况不太常见。讽刺的是,我们使用占位符匹配表名称中的“_”和“2”字符。

在文章的开头,我承诺展示一种更可靠的方法来处理该任务,所以这里是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
sql复制代码mysql> SHOW TRIGGERS WHERE `Table` = 'test2lab';
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
| Trigger | Event | Table | Statement | Timing | Created | sql_mode | Definer | character_set_client | collation_connection | Database Collation |
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
| test2lab_trigger_INS | INSERT | test2lab | BEGIN
INSERT INTO log_lab (`changed_table`,`id`) VALUES ('test2lab', NEW.id) ;
END | AFTER | 2024-04-13 10:35:45.29 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION | msandbox@localhost | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci |
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
1 row in set (0.01 sec)

mysql> SHOW TRIGGERS WHERE `Table` = 'test_lab';
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
| Trigger | Event | Table | Statement | Timing | Created | sql_mode | Definer | character_set_client | collation_connection | Database Collation |
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
| test_lab_trigger_INS | INSERT | test_lab | BEGIN
INSERT INTO log_lab (`changed_table`,`id`) VALUES ('test_lab', NEW.id) ;
END | AFTER | 2024-04-13 10:35:45.29 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION | msandbox@localhost | utf8mb4 | utf8mb4_0900_ai_ci | utf8mb4_0900_ai_ci |
+----------------------+--------+----------+------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------------------------------------------+--------------------+----------------------+----------------------+--------------------+
1 row in set (0.00 sec)

您可以过滤输出中的任何其他列,不限于表。不要忘记使用 Table,因为它是 MySQL 中的保留字,如果不使用它们(以及大多数其他列名),您将无法逃脱。

如果您想做自己的实验,我将留给读者使用 VIEW 进行相同的测试。

题外话

这时,你可能会问这个极端案例与现实世界有什么关系。当一个包含下划线而另一个包含数字时,表名称略有不同的情况并不常见。除了无法获得您愿意获得的输出之外,这也是 mydumper/myloader 中出现错误的原因,我在此报告:

github.com/mydumper/my…

Mydumper 将每个表的触发器放入单独的文件中,并使用 LIKE 语句导致属于 test2lab 的触发器最终出现在 test_lab 触发器文件中。

结论

本文介绍了了解运算符如何在 LIKE 子句示例中精确执行的重要性、如何创建快速实验室来测试命令的结果以确保结果,以及如何使用运算符替代 LIKE。

更多技术文章,请访问:opensource.actionsky.com/

关于 SQLE

SQLE 是一款全方位的 SQL 质量管理平台,覆盖开发至生产环境的 SQL 审核和管理。支持主流的开源、商业、国产数据库,为开发和运维提供流程自动化能力,提升上线效率,提高数据质量。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

(已解决)no “render_mode“和got an u

发表于 2024-04-22

@[TOC]

前言:

清早起来,验收晚上跑的马里奥模型,一测试,发现炼丹炉爆炸了。
先后出现了
warnings.warn("You tried to call render() but no "render_mode" was passed to the env constructor.")

Box(0, 255, (4, 240, 256), uint8) observation space is not supported 等问题,下文将分析如何解决,请通读全文,再进行安装尝试!!。


问题1. render的render_mode问题

当我在运行render()的时候,遇到了这个问题
warnings.warn("You tried to call render() but no ‘render_mode’ was passed to the env constructor.")
意思是说你要运行render,怎么能不告诉我render_mode呢?那我们就加上。

1
python复制代码environment = gym_super_mario_bros.make('SuperMarioBros-v0',render_mode = "human")

怎么还是寄!遇到以下问题
TypeError: SuperMarioBrosEnv.__init__() got an unexpected keyword argument 'render_mode'
这就好笑了,超级玛丽的库没写render_mode而我们调用却需要!!这就十分吊诡了。
这是由于库更新了导致的,我们需要重新配置库。
笔者广阅群文,发现完全没有人说这个问题,在各种评论区畅游得到一个解决方案如下:

1
2
3
4
5
bash复制代码pip install nes-py
pip install gym-super-mario-bros==7.3.0
pip install setuptools==65.5.0 "wheel<0.40.0"
pip install gym==0.21.0
pip install stable-baselines3[extra]==1.6.0

问题2. baselines3版本问题

大喜过望,开始尝试,运行最后一个的时候

ERROR: No matching distribution found for ale-py==0.7.4; extra == "extra"

在这里插入图片描述
¯﹃¯ (博主当时表情)
说是缺少ale-py库,寄。于是我们再次安装ale-py==0.7.4
在这里插入图片描述
????啊???0.7.4版本,他没了!¯﹃¯
再pypl上发现官方只有8版本以上了,经过尝试,我们可以退而求其次安装

1
python复制代码pip install baselines3==1.6.0

安装成功!

问题3. cloudpickle版本问题

我们安装好了之后再次运行,之前的问题也确实没有了,又遇到了
在这里插入图片描述

或者NotImplementedError: Box(0, 255, (4, 240, 256), uint8) observation space is not supported
这是由于cloudpickle版本和训练版本不对应,或者cloudpickle版本不对导致的。
请卸载cloudpickle,请输入

1
python复制代码pip install cloudpickle==3.0.0

然后按照马里奥系列第三章马里奥游戏ai训练与保存重新训练与当前版本一致的模型就好啦~


完整解法

如果按照以下解法安装仍然失败,可能是由于库连体的问题,就是一些库比如baselines3和gym会附带安装其他库,请你也注意其版本,如果需要,可以删除重装。如果还是不行,建议把所有相关库删掉,然后依次执行以下代码。

1
2
3
4
5
6
py复制代码pip install nes-py
pip install gym-super-mario-bros==7.3.0
pip install setuptools==65.5.0 "wheel<0.40.0"
pip install gym==0.21.0
pip install stable-baselines3==1.6.0
pip install cloudpickle==3.0.0

如果还是不行,我附上我的各个库的版本(相关的),请根据自己的需要查看。输入pip list 即可查看自己的库。这些库的版本如果都对,是可以运行render之类的显示指令的。请一一对应检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
c复制代码ale-py                     0.8.1
atari-py 0.2.6
cloudpickle 3.0.0
gym 0.21.0
gym-notices 0.0.8
gym-super-mario-bros 7.3.0
gymnasium 0.28.1
importlib-metadata 4.13.0
importlib_resources 6.4.0
MarkupSafe 2.1.3
matplotlib 3.8.2
matplotlib-inline 0.1.6
nes-py 8.2.1
numpy 1.26.0
opencv-contrib-python 4.9.0.80
opencv-python 4.9.0.80
pip 24.0
pygame 2.5.2
setuptools 65.5.0
Shimmy 1.3.0
tensorboard 2.16.2
tensorboard-data-server 0.7.2
tensorboardX 2.6.2.2
torch 2.1.2+cu118
torchaudio 2.1.2+cu118
torchvision 0.16.2+cu118
wheel 0.38.4

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

actor 是线程安全的么?

发表于 2024-04-22

actor 是线程安全的么?

The Actor Reentrancy Problem in Swift

不是的。考虑reentrance的情况

suspension point是否在actor里,如果有的话,那么在并发的两个task,就有可能会发生重入(Reentrance)的情况。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
swift复制代码actor BankAccount {
   
   private var balance = 1000
   
   func withdraw(_ amount: Int) async {
       
       print("🤓 Check balance for withdrawal: (amount)")
       
       guard canWithdraw(amount) else {
           print("🚫 Not enough balance to withdraw: (amount)")
           return
       }
       
       guard await authorizeTransaction() else {
           return
       }
       
       print("✅ Transaction authorized: (amount)")
       
       balance -= amount
       
       print("💰 Account balance: (balance)")
   }
   
   private func canWithdraw(_ amount: Int) -> Bool {
       return amount <= balance
   }
   
   private func authorizeTransaction() async -> Bool {
       
       // Wait for 1 second
       try? await Task.sleep(nanoseconds: 1 * 1000000000)
       
       return true
   }
}

let account = BankAccount()

Task {
   await account.withdraw(800)
}

Task {
   await account.withdraw(500)
}

我们可以想象一下,这个会输出什么结果?3,2,1

乍一看,我们会想,先执行完await account.withdraw(800),然后剩余200,然后执行await account.withdraw(500),发现余额不足,不执行。

但是结果如下:

1
2
3
4
5
6
yaml复制代码🤓 Check balance for withdrawal: 800
🤓 Check balance for withdrawal: 500
✅ Transaction authorized: 800
💰 Account balance: 200
✅ Transaction authorized: 500
💰 Account balance: -300

你看,余额变成了-300,这明显与我们的认知不符合。那么为什么会出现的结果呢,其实是因为,actor只是能保证不出现数据竞争,但是我们的withdraw方法都会在authorizeTransaction挂起,那么也就是意味着,我们并没有在一个task执行完,才去执行另一个。也就是说,它并不会去判断挂起的时候,可变的状态还是保持你刚进入task的状态,可能有点拗口,那拿这个例子举例,就是actor不会保证,balance会自动修改为task1执行完后的值,也就是200。所以我们在做balance -= amount操作的时候,需要再判断一下,余额是否满足条件,这也是apple建议我们做的,即始终保持actor的状态变更是同步的。

即:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
swift复制代码func withdraw(_ amount: Int) async {
   
   // Perform authorization before check balance
   guard await authorizeTransaction() else {
       return
   }
   print("✅ Transaction authorized: (amount)")
   
   print("🤓 Check balance for withdrawal: (amount)")
   guard canWithdraw(amount) else {
       print("🚫 Not enough balance to withdraw: (amount)")
       return
   }
   
   balance -= amount
   
   print("💰 Account balance: (balance)")
   
}

但是,假设authorizeTransaction方法非常耗时,而余额很明显是不满足扣款条件的,就是一种资源的浪费。所以我们可以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
swift复制代码func withdraw(_ amount: Int) async {
   
   print("🤓 Check balance for withdrawal: (amount)")
   guard canWithdraw(amount) else {
       print("🚫 Not enough balance to withdraw: (amount)")
       return
   }
   
   guard await authorizeTransaction() else {
       return
   }
   print("✅ Transaction authorized: (amount)")
   
   // Check balance again after the authorization process
   guard canWithdraw(amount) else {
       print("⛔️ Not enough balance to withdraw: (amount) (authorized)")
       return
   }

   balance -= amount
   
   print("💰 Account balance: (balance)")
   
}

虽然多做了一次canWithdraw,但是可以提升效率,且避免了重入的问题。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

斯坦福HAI年度报告增加AI4S;美阿贡国家实验室与日本最大

发表于 2024-04-22

AI for Science 企业动态速览——

  • Cota Healthcare 与赛诺菲达成合作
  • 腾讯牵头共建医疗影像国家新一代人工智能开放创新平台
  • 催化剂加获得深势科技未知金额投资
  • TetraScience 与 Google Cloud 合作促进科学人工智能创新
  • 美国阿贡国家实验室和日本理化学研究所签署谅解备忘录
  • 《2024 年人工智能指数报告》增加 AI4S 相关内容
  • 美年健康:国内首款健康管理 AI 机器人试运行

Cota Healthcare 与赛诺菲达成合作

4 月 17 日,肿瘤学分析领域企业 Cota Healthcare 宣布与赛诺菲达成合作,双方将基于 AI 利用真实世界数据 (RWD) 提高肿瘤学药物研发效率。Cota Healthcare 公司成立于 2011 年,团队由医生、工程师和大数据学家共同组成,致力于以肿瘤学数据分析为核心,通过大数据技术进行临床数据和成本分析,为肿瘤患者提供最佳的治疗方案。赛诺菲成立于 1973 年,是一家创新型全球医药健康公司,主要从事药品研发、生产和销售。

腾讯牵头共建医疗影像国家新一代人工智能开放创新平台

近日,「医疗影像国家新一代人工智能开放创新平台建设」顺利结题。该平台建设由腾讯公司牵头,中国科学院深圳先进技术研究院、广州互云医、郑州大学第一附属医院、南方医科大学南方医院、上海全景医疗影像诊断中心等 10 家单位共同出力。现已完成共性技术平台建设,开源医疗数据库、算法资源库建设和行业标准体系建设,实现了医疗影像 AI 从研发到落地的全流程打通。

催化剂加获得深势科技未知金额投资

近日,催化剂加 (CatalystPlus) 宣布获得深势科技战略投资,具体投资金额暂未披露。此次投资将促使双方在 AI for Science 领域展开全面合作,合作涉及科学大模型、计算能力、知识库、数据库、用户发展等多个方面。深势科技成立于 2019 年,主要业务方向是打造切实服务于药企、材料商和科研机构的模拟研发平台。催化剂加 (CatalystPlus) 成立于 2022 年,是一家企业解决方案提供商,整合了十亿级学术基础数据,涵盖论文、学者、会议、图表、专利、基金等多样化数据类型,致力于帮助科学家更高效地处理多维度信息、解决信息过载**问题。

TetraScience 与 Google Cloud 合作促进科学人工智能创新

近日,TetraScience 宣布与 Google Cloud 达成合作,双方将把 Tetra 的科学数据、AI Cloud 与 Google Cloud 的基础设施、AI 技术相结合,共同增强生命科学行业对科学 AI 的应用,加速药物开发和其他科学发现。据悉,TetraScience 是一家科学数据和 AI Cloud 计算公司,公司致力于设计和产业化 AI-native 科学数据集,并将其应用于下一代实验室数据管理解决方案、科学用例和 AI-based 科学成果中,以此来推动科学人工智能的发展。

美国阿贡国家实验室和日本理化学研究所签署谅解备忘录

近日,美国阿贡国家实验室 (Argonne) 和日本理化学研究所 (RIKEN**) 宣布签署谅解备忘录,正式建立合作关系。据悉,双方已同意共享研究人员、数据集和技术资源,未来将共同基于科学数据训练大语言模型,支持后续 AI for science 研究。RIKEN 成立于 1917 年,是日本最大的综合性研究机构,已在多学科领域开展高质量研究。Argonne National Laboratory 是美国第一个国家实验室,汇聚了来自 60 多个国家的员工,与数百家公司、大学和联邦的研究人员展开密切合作,在几乎所有科学领域都进行过前沿科学研究。

《2024 年人工智能指数报告》增加 AI4S 相关内容

近日,斯坦福大学 Human-Center Artificial Intelligence (HAI) 研究中心发布了《2024 年人工智能指数报告》,这份超过 300 页的报告新增了关于 AI 在科学和医学领域中应用的章节。报告指出,自 2022 年以来,AI for Science 就呈加速发展趋势,2023 年诞生了许多与其他科学相关的 AI 应用,比如提高算法排序效率的 AlphaDev,促进材料发现过程的 GNoME,用于天气预报 GraphCast,以及AI 驱动医疗创新的 SynthSR 和 ImmunoSEIRA。

美年健康:国内首款健康管理 AI 机器人试运行

4 月 15 日,国内首款健康管理 AI 机器人:「健康小美」数智健管师启动试运行,该产品预计 6 月底将正式发布。据悉,「健康小美」是美年健康联合华为云、润达医疗共同研发的 AI 机器人,用户只需上传个人生活习惯、运动情况等信息,就能一键生成适合自己的健康管理计划。此外,用户还能根据个人的健康数据、家族病史等获取专属的体检和预防措施。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

iOS - 多线程-GCD iOS - 多线程-GCD

发表于 2024-04-22

iOS - 多线程-GCD

  1. 常见多线程方案

NSThread、GCD、NSOperation底层都依赖于pthread

  1. GCD

2.1 GCD的常见函数

GCD中有2个用来执行任务的函数

  • 用同步的方式执行任务
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
1. queue:队列
2. block:任务
  • 用异步的方式执行任务
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
  • GCD源码:github.com/apple/swift…

2.2 GCD的队列

2.2.1 GCD的队列可以分为2大类型

  • 并发队列(Concurrent Dispatch Queue)
1. 可以让多个任务`并发(同时)`执行(自动开启多个线程同时执行任务)
2. `并发`功能只有在`异步`(`dispatch_async`)函数下才有效
  • 串行队列(Serial Dispatch Queue)
1. 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

2.3 容易混淆的术语

2.4.1 有4个术语比较容易混淆:同步、异步、并发、串行

  • 同步和异步主要影响:能不能开启新的线程
    • 同步:在当前线程中执行任务,不具备开启新线程的能力
    • 异步:在新的线程中执行任务,具备开启新线程的能力
  • 并发和串行主要影响:任务的执行方式
    • 并发:多个任务并发(同时)执行
    • 串行:一个任务执行完毕后,再执行下一个任务

2.4 各种队列的执行效果

  • 使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)
  1. 死锁

3.1 死锁示例

1
2
3
4
5
6
7
objectivec复制代码// 问题:以下代码是在主线程执行的,会不会产生死锁?
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"执行任务2");
});
NSLog(@"执行任务3");

3.2 死锁分析

如上代码

  • 正常情况应该是顺序执行任务1、任务2、任务3
  • 任务2使用dispatch_sync同步执行方式,放入主线程队列,因此任务2需要排队等待前面的任务执行完成后才执行
  • 但是当前方法体viewDidLoad可以认为就是一个任务在执行,但是执行到任务2的dispatch_sync处,会等待dispatch_sync执行完成再继续往下执行
  • 此时,相当于任务2等待当前执行任务执行完成,当前执行任务也在等待任务2执行完成,相互等待因此造成线程死锁

3.3 其他示例

3.3.1 interview02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
objectivec复制代码- (void)interview02 {
// 问题:interview01中的sync,改成 async。会不会产生死锁?不会!
/* 打印日志:
执行任务1
执行任务3
执行任务2
*/
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"执行任务2");
});
NSLog(@"执行任务3");
}

3.3.2 interview03

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
objectivec复制代码- (void)interview03 {
// 会不会产生死锁?会!
/*
分析:
执行任务2 后,同步等待任务2 执行,但是因为queue是串行的,所以会相互等待
*/
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"执行任务2");
dispatch_sync(queue, ^{
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
}

3.3.3 interview04

1
2
3
4
5
6
7
8
9
10
11
12
13
objectivec复制代码- (void)interview04 {
// interview03改为并发队列(DISPATCH_QUEUE_CONCURRENT),会不会死锁?不会!
NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"执行任务2");
dispatch_sync(queue, ^{
NSLog(@"执行任务3");
});
NSLog(@"执行任务4");
});
NSLog(@"执行任务5");
}
  1. 案例

4.1 案例1

如下代码打印什么:

1
2
3
4
5
6
7
8
9
10
11
12
objectivec复制代码- (void)test {
NSLog(@"2");
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:0];
NSLog(@"3");
});
}

打印结果:

观察到,2不会打印

去掉dispatch_async又会怎么样

1
2
3
4
5
objectivec复制代码- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:0];
NSLog(@"3");
}


这时候都有打印,只不过打印顺序1>3>2

接着,回到dispatch_async里执行,但是把afterDelay去掉

1
2
3
4
5
6
7
8
objectivec复制代码- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil];
NSLog(@"3");
});
}


打印结果是1>2>3,这次打印顺序是正常的

4.2 分析

上面的例子中,主要是考察runloop与多线程的相关知识

  • 首先,在dispatch_async中使用- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;方法来执行,使用afterDelay:0看似是在没有延迟的情况下执行,实际上因为该方法是基于 runloop的,相当于往runloop添加一个定时器,但是因为此时我们是在子线程中执行的,子线程中的runloop默认不会开启,所以test方法没有执行。我们尝试开启runloop
1
2
3
4
5
6
7
8
objectivec复制代码dispatch_async(queue, ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:0];
NSLog(@"3");

[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
});

可以看到,2打印了

  • 接着,我们去掉了dispatch_async,发现打印结果是1>3>2,为什么3会比2先打印的?

使用afterDelay:0看似是在没有延迟的情况下执行,实际上因为该方法是基于 runloop的定时器,虽然没有延迟设置为 0,但是runloop的定时器是在被唤醒的时候处理定时器的,但是在进入休眠之前会处理完点击事件,因此看到的打印结果是1、3先打印,然后打印2

  • 最后,回到dispatch_async中执行,只不过使用的是- (id)performSelector:(SEL)aSelector withObject:(id)object;方法来执行,查看源码

    该方法实际是直接使用objc_msgSend方法执行,相当于我们直接[self test]这样调用方法,所以这时候打印顺序是正常的1>2>3
  1. 拓展

GNUstep

GNUstep是GNU计划的项目之一,它将Cocoa的OC库重新开源实现了一遍

源码地址:gnustep.github.io/resources/d…

虽然GNUstep不是苹果官方源码,但还是具有一定的参考价值

@oubijiexi

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…343536…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%