我在网上搜索并阅读了Boost文档shared_ptr.在SO上有一个响应,表示shared_ptr对于写入时复制(COW)糟透了,TR!并且已将其从字符串库中删除.关于SO的大多数建议都说使用shared_ptr而不是常规指针.
该文档还讨论了使用std::unique()COW指针,但我没有找到任何示例.
是否有关于为您执行COW的智能指针或让您的对象使用新shared_ptr的克隆对象然后修改克隆对象的讨论?
struct Nutrients;
struct Ingredient
{
Ingredient(const std::string& new_title = std::string(""))
: m_title(new_title)
{ ; }
std::string m_title;
Nutrients ing_nutrients;
};
struct Milk : public Ingredient
: Ingredient("milk")
{ ; }
struct Cream : public Ingredient
: Ingredient("cream")
{ ; }
struct Recipe
{
std::vector< boost::shared_ptr<Ingredient> > m_ingredients;
void append_ingredient(boost::shared_ptr<Ingredient> new_ingredient)
{
m_ingredients.push_back(new_ingredient);
return;
}
void replace_ingredient(const std::string& original_ingredient_title,
boost::shared_ptr<Ingredient> new_ingredient)
{
// Confusion here …Run Code Online (Sandbox Code Playgroud) 这是我的代码
int main()
{
pid_t pid;
int y = 3;
if ( (pid = fork()) <0 )
return -1;;
if( pid == 0 ) /* child */
{
printf(" before: %d %p\n", y, &y );
y *= 10;
printf("after: %d %p\n", y, &y );
}
else /* father */
{
sleep(1);
printf("father: %d %p\n" , y , &y );
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
该程序的输出如下:
before: 3 ffbff440
after: 30 ffbff440
father: 3 ffbff440
Run Code Online (Sandbox Code Playgroud)
我的问题是为什么孩子和父母的变量的地址相同但价值不同?
我有一个大缓冲区:
char *buf = malloc(1000000000); // 1GB
Run Code Online (Sandbox Code Playgroud)
如果我分叉一个新进程,它将有一个buf,它与父进程的buf共享内存,直到一个或另一个写入它.即使这样,内核也只需要分配一个新的4KiB块,其余的将继续共享.
我想复制一下buf,但我只想改变一点副本.我想要不写分支的写时复制行为.(就像你在分叉时免费获得的那样.)
这可能吗?
如果我在多线程环境中有一个不同步的java集合,并且我不想强制集合的读者同步[1],那么我是一个同步编写器并使用引用赋值的原子性可行的解决方案吗?就像是:
private Collection global = new HashSet(); // start threading after this
void allUpdatesGoThroughHere(Object exampleOperand) {
// My hypothesis is that this prevents operations in the block being re-ordered
synchronized(global) {
Collection copy = new HashSet(global);
copy.remove(exampleOperand);
// Given my hypothesis, we should have a fully constructed object here. So a
// reader will either get the old or the new Collection, but never an
// inconsistent one.
global = copy;
}
}
// Do multithreaded reads here. …Run Code Online (Sandbox Code Playgroud) 所以我有一个简单的cow_ptr.它看起来像这样:
template<class T, class Base=std::shared_ptr<T const>>
struct cow_ptr:private Base{
using Base::operator*;
using Base::operator->;
using Base::operator bool;
// etc
cow_ptr(std::shared_ptr<T> ptr):Base(ptr){}
// defaulted special member functions
template<class F>
decltype(auto) write(F&& f){
if (!unique()) self_clone();
Assert(unique());
return std::forward<F>(f)(const_cast<T&>(**this));
}
private:
void self_clone(){
if (!*this) return;
*this = std::make_shared<T>(**this);
Assert(unique());
}
};
Run Code Online (Sandbox Code Playgroud)
这可以保证它拥有一个非常量T并确保它在unique何时出现.write([&](T&){}).
在C++ 17的折旧.unique()似乎表明这种设计是有缺陷的.
我猜测如果我们从线程A中的cow_ptr<int> ptrwith 开始1,将它传递给线程B,使其唯一,修改它2,传ptr回它并在线程中读取它A我们已经生成了竞争条件.
我该如何解决?我可以简单地添加内存屏障write吗?哪一个?或者问题更根本? …
我想拥有功能数据结构的优势(可以共享结构的多个数据版本),但能够以命令式方式修改它.
我正在考虑的(以及可能的用途):一个RPG游戏,其中存储了整个游戏历史(例如,允许回到过去).使用copy-on-write,我可以简单地克隆保持游戏状态的结构并通过引入新的转弯来修改它 - 但是可以访问较早的转弯(不一定是所有这些转弯,可能只是游戏状态的选定快照),而不是每次必须复制一切的惩罚.
让我们说foo是一张地图.
bar = foo.clone()
Run Code Online (Sandbox Code Playgroud)
没有任何foo结构(例如,树)被复制.但是,从现在开始,bar它被视为副本,并且不允许任何更改传播回`foo'.
baz = bar[someKey]
baz.modifyInSomeWay()
Run Code Online (Sandbox Code Playgroud)
现在
baz.bar用新地图替换,保留新的baz(可能保留一些foo结构).foo 不受影响.但如果我们那么做......
baz.modifyAgain()
Run Code Online (Sandbox Code Playgroud)
... baz可以修改,因为我们有最新版本的.bar
不需要改变.
所有这些都需要持有的一些版本信息foo和bar关于增加它foo.clone(),并把它传递给baz某种方式(通过使代理对象?).
此外,已克隆的结构的任何部分都成为"历史的一部分",不能再被更改,这可以在运行时强制执行.
这有点类似于JavaScript的原型,但我更多的是因为它允许更改向上传播.我认为它会像版本控制系统.
如果我们考虑使用引用计数的std :: string实现,请考虑以下情况:
int main()
{
string english = "Hello";
string german = english; //refcnt = 2
string german2 = german;
/* L1 */ german[1] = 'a';
/* L2 */ *(german2.begin() + 1) = 'A';
cout << english << endl << german << endl << german2 << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
L1和L2会发生什么?引用计数是否已损坏并执行了深层复制?我是这么认为的,但我担心的是,如果发生这种情况,做一个简单的事情:
cout << german[1] << endl;
Run Code Online (Sandbox Code Playgroud)
还是简单的:
cout << *(german.begin()) << endl;
Run Code Online (Sandbox Code Playgroud)
在非const上下文中将执行不必要的深层复制.我对吗?这些实现如何处理这个细节?
我想知道如何在fork()中发生copy-on-write.
假设我们有一个具有动态int数组的进程A:
int *array = malloc(1000000*sizeof(int));
Run Code Online (Sandbox Code Playgroud)
数组中的元素初始化为一些有意义的值.然后,我们使用fork()创建一个子进程,即B.B将迭代数组并进行一些计算:
for(a in array){
a = a+1;
}
Run Code Online (Sandbox Code Playgroud)
a = a+1?a = a+1;这是怎么发生的?B是否从A读取数据并将新数据写入其自己的数组?我写了一些代码来探索COW如何工作.我的环境:ubuntu 14.04,gcc4.8.2
#include <stdlib.h>
#include <stdio.h>
#include <sys/sysinfo.h>
void printMemStat(){
struct sysinfo si;
sysinfo(&si);
printf("===\n");
printf("Total: %llu\n", si.totalram);
printf("Free: %llu\n", si.freeram);
}
int main(){
long len = 200000000;
long *array = malloc(len*sizeof(long));
long i = 0;
for(; i<len; i++){
array[i] = i;
}
printMemStat();
if(fork()==0){
/*child*/
printMemStat();
i = 0;
for(; i<len/2; i++){
array[i] = i+1; …Run Code Online (Sandbox Code Playgroud) str::to_ascii_lowercase返回一个字符串。为什么它不返回Cow<str>类似to_string_lossyor 的值String::from_utf8_lossy?
这同样适用于str::to_ascii_uppercase.
这个问题包含一些解决该问题的建议,我想更深入地了解问题到底是:
QList<QString> q;
for (QString &x: q) { .. }
Run Code Online (Sandbox Code Playgroud)
const,否则 Qt 是否会创建列表的副本,然后迭代该副本?这不是最好的,但如果列表很小(比如 10-20 个 QString),这是可以忍受的。copy-on-write ×10
c++ ×4
c ×3
fork ×3
unix ×2
api-design ×1
boost ×1
c++17 ×1
foreach ×1
immutability ×1
java ×1
linux ×1
locking ×1
python ×1
qt ×1
rust ×1
shared-ptr ×1
stl ×1
string ×1