参数所有权与共享原子
概述
DIP25和DIP1000中提出的语言特征通过检测指针是否超出了函数域
,大大提高了给函数传递引用和指针
的内存安全.相应,如果函数不允许
转义引用,则容器可以安全地传递内部引用给函数.
但,如果传递多个相同容器的引用给函数,则一个引用会渲染其他引用引用的空数据.该DIP旨在纠正该问题.这是在DIP 25和DIP 1000后的自然发展,是安全实施引用计数
所必需的.这项提议是迈向博客文章"所有权和借款在D中"(中文在此)概述的万里长征的第一步.
理由
如果不能内存安全
的访问容器有效载荷,容器是内存不安全的.如果容器无法直接引用其有效负载,则无法高效运行.让用户不做某些事情
是不可靠的,也无法扩展.
问题最简单说明:
struct S {
字节* ptr;
ref byte get(){ return *ptr; }
}
void foo(ref S t,ref byte b){
释放(t.ptr); //释放b引用内存
b = 4 ;//已坏的内存访问
}
空 测试(){
s;
s.ptr = 转换(字节*)malloc(1);
foo(s,s.get()); //(*)
}
使用域指针
的相同问题:
struct S {
字节* ptr;
字节* get(){ return ptr; }
}
void foo(域 S*t,域 字节* pb){//两个参数指向相同内存
释放(t.ptr); //释放pb引用的内存
*pb = 4 ; //破坏的内存访问
}
空 测试(){
s;
s.ptr = 转换(字节*)malloc(1);
foo(&s,s.get()); //(*)
}
D当前不能防御这种问题,因此不能机械检查内存安全引用计数.(Timon Gehr首先指出这点.)
先前的工作
Rust这样避免问题的:
-
任何借不能超过所有者的域.
-
你可能有1到2种借,但不能2种一样.
-
同一资源的一/多个引用(&T),
-
只能一个可变引用(&mut T).
引用地址
描述
该方法在于:示例中,给函数foo
传递了同一数据的两个可变引用(即不允许同一个资源的多个可变引用
).即,只要同一数据有多个引用,且其中一个是可变的,则无论可变引用
是不是常
,都能使数据空.因此,如果将多个同一数据的引用传递给一个函数时,它们必须都为常
.
DIP25和DIP1000在编译器语义已收集的数据
上,加额外检查
,以通过函数调用和返回
来跟踪生命期.
这是在上面两个dip上
建立和测试的基础上构建的.
仅检查@安全
代码.
句法
此DIP建议不更改语法.它在现有构造上加了其他语义
检查.
局限
提交特征仅检查函数调用
表达式.它不检查之间的状态
.不检查非域针
.尽管这是重要一步,但不是完整借贷/所有权
机制.例如,可用临时项来取消检查域指针
:
空 测试(){
S s;
s.ptr = 转换(字节*)malloc(1);
动 ps =&s;
foo(ps,s.get()); //(*) `ps,与s是相同针`
}
解决此问题只能使用"到达定义"的对编译器帮助很大的数据流分析
.另外,引用不需要它,因为只初化它们一次且始终在域内.
重大变更和过时
这将破坏传递多个相同对象的可变引用
给函数的现有代码,不知道这种模式有多普遍.可将代码标记为@信任,@系统
来修复.
讨论区
一些评论者抱怨说,DIP缺乏细节,并且提供的示例来说明DIP解决的问题是不够的.特别是,@safe由于DIP声明提出的检查仅在@safe代码中执行,因此请求使用代码说明问题的示例.DIP作者回答说,"先前的工作"部分中包含额外的信息,并且提供的示例
足以说明问题.
讨论区
社区评论对缺乏细节的一些批评被重复了,有人抗议没有对这些反馈
做出修改.有人对"大局"计划(如作者在本博客文章中概述的)以及该建议如何适合其中提出了疑问.关于Rust实现所有权和借用
以及讨论D是否适合
他.
共享原子
概述
核心语言的操作符不支持读写类型为共享
的数据.只能通过库中的函数调用来访问它们.
理由
使共享类型
成为D中的一流类型是项有效的创新.对开发健壮的多线程应用程序
区分共享和非共享数据
的能力至关重要.但D缺少改变访问共享数据的语义
的方法,
由于优化编译器的代码动作,使默认行为遭受数据竞争的既明显(速度慢)又隐藏(看不见)
的影响.
禁止直接访问共享数据,要求用户正确使用core.atomic
.
描述
现在要求程序员使用core.atomic
或等效
函数来读写共享内存对象
.直接读写共享内存对象
时,编译器将报告错误.避免意外,无意
地不使用原子访问
.
允许初化
共享数据.语法不变.
例子:
共享 int x;// 好,初化为0
++x;//错,读写共享x
共享 int* p = &x;// 好, 初化共享 p
共享 int* q = p; // 错, 读共享p//赋值
共享(int)* r = &x; // 好
共享(int)* s = r; // 好
*s = 3; //错,写至共享 *s
int y = *s;//错,从共享读*s
局限性
该提议不保证代码无锁,也不能消除事务竞争
时锁的需求.(事务是必须执行的一系列,完成事务前,其他线程不能改共享数据
的操作.具有原子性)
它不禁止转换共享数据为非共享数据,然后通过核心语言运算符操作它,尽管仅允许@system和@trusted代码
操作.
替代
用CPU支持的操作符对锁定操作提供有限支持.C++就这样.这有争议,因为有人认为这鼓励错误的编码做法.
重大变更和弃用
所有访问共享内存对象
的代码都会中断.因而会有很长的淘汰周期.
用core.atomic
的代码没问题.
为了生成常规代码,锁保护代码需要通过转换()式
或转换(常)式
来去掉类型头部的"共享".用户必须小心,不要让那些头未共享的内存位置的引用
脱离锁定代码区域,但在本dip前保持原样.
讨论区
重大的,反复投诉集中在文本的矛盾部分,引起了审阅者的困惑.DIP作者解释说是编辑过程中发生的错误,下一个版本中修改.除要求作者包含其他语言的引用
外,其余讨论都涉及相关主题(例如,共享
的当前和预期行为).
讨论区
在本轮审核中,仅提供了两项可行反馈意见:可能破坏代码示例;并且以"原子读取执行获取操作…"开头的句子与该建议无关,应该删除.DIP作者对此进行了修改.
讨论区
该提议共享仍可与BetterC
一起使用.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现