在构造函数中引用此指针
Referencing this pointer in constructor
Following is another example of forward declaration, which might be
useful if the application needs a self-sustaining array of objects
which is able to add and remove objects from itself during run-time:
File a.h:
class A {
public:
static A *first, *last;
A *previous, *next;
A();
~A();
};
File a.cpp:
#include "a.h"
A *A::first=0, *A::last=0; // don't put the word static here, that will cause an error
A::A() {
if(first==0) first=this; //first A created
previous=last;
if(previous != 0) previous->next=this;
last=this;
next=0;
}
A::~A() {
if(previous != 0) previous->next=next;
if(next != 0) next->previous=previous;
}
发件人:https://en.wikipedia.org/wiki/Circular_dependency#Self-reference_example
我认为 A
的实施存在问题。在构造 A
的第一个实例时,如果其他线程引用 A::first
,则会导致意外行为。如果我错了请更正。
另外,请问如何解决这个问题?谢谢。
您需要将 first
和 last
变量声明为原子变量,例如使用 std::atomic
或者用 mutex
保护它们
是的,没错。他们需要受到保护。此外,previous
和 next
也需要保护。例如
A::~A() {
unique_lock<std::mutex> locked(A::A_mutex);
if(previous != 0) previous->next=next;
if(next != 0) next->previous=previous;
}
其中 A_mutex
是静态成员。
正如其他人在评论中所述,此代码绝对是非线程安全的。无论是否是第一个对象,如果有 2 个线程同时尝试创建或删除 A 对象,您会得到未定义的行为,因为两个不同的线程在没有任何同步的情况下使用和更改相同的静态值。
可以做什么?一如既往地有两个相同的选项:
- 记录 class(至少构造函数和析构函数)不是线程安全的。由于它们,调用者有责任确保只有一个线程可以同时访问 A 对象。换句话说,这意味着A只有在单线程程序中才会安全。
- 在 class 自身内部添加同步以使其线程安全。当创建和销毁操作静态成员时,您需要 class 的所有对象的全局互斥锁,换句话说 class 静态对象。
正如@zvone 在评论中注意到的那样,当您删除链中的第一个或最后一个元素时,析构函数可以使 first
和 last
成为悬挂指针。
析构函数(非线程安全版本)应该是:
A::~A() {
if(previous != 0) previous->next=next;
if(next != 0) next->previous=previous;
if (first == this) first = next;
if (last == this) last = previous;
}
线程安全版本可以是:
文件a.h
class A {
public:
static A *first, *last;
A *previous, *next;
static std::mutex mut;
A();
~A();
};
文件a.cpp:
#include "a.h"
A *A::first=0, *A::last=0; // don't put the word static here, that will cause an error
std::mutex A::mut;
A::A() {
mut.lock()
if(first==0) first=this; //first A created
previous=last;
if(previous != 0) previous->next=this;
last=this;
next=0;
mut.unlock();
}
A::~A() {
mut.lock()
if(previous != 0) previous->next=next;
if(next != 0) next->previous=previous;
if (first == this) first = next;
if (last == this) last = previous;
mut.unlock();
}
这里答案的问题是他们没有指出 previous 和 next 是 public 成员的事实 - 这意味着,构造函数和析构函数中的互斥锁没有保护可以使 class 完全线程安全。使这个 class 线程安全的唯一方法是隐藏这些成员并提供访问它们的方法 - 每个内部都有一个同步原语。
Following is another example of forward declaration, which might be useful if the application needs a self-sustaining array of objects which is able to add and remove objects from itself during run-time:
File a.h:
class A { public: static A *first, *last; A *previous, *next; A(); ~A(); };
File a.cpp:
#include "a.h" A *A::first=0, *A::last=0; // don't put the word static here, that will cause an error A::A() { if(first==0) first=this; //first A created previous=last; if(previous != 0) previous->next=this; last=this; next=0; } A::~A() { if(previous != 0) previous->next=next; if(next != 0) next->previous=previous; }
发件人:https://en.wikipedia.org/wiki/Circular_dependency#Self-reference_example
我认为 A
的实施存在问题。在构造 A
的第一个实例时,如果其他线程引用 A::first
,则会导致意外行为。如果我错了请更正。
另外,请问如何解决这个问题?谢谢。
您需要将 first
和 last
变量声明为原子变量,例如使用 std::atomic
或者用 mutex
是的,没错。他们需要受到保护。此外,previous
和 next
也需要保护。例如
A::~A() {
unique_lock<std::mutex> locked(A::A_mutex);
if(previous != 0) previous->next=next;
if(next != 0) next->previous=previous;
}
其中 A_mutex
是静态成员。
正如其他人在评论中所述,此代码绝对是非线程安全的。无论是否是第一个对象,如果有 2 个线程同时尝试创建或删除 A 对象,您会得到未定义的行为,因为两个不同的线程在没有任何同步的情况下使用和更改相同的静态值。
可以做什么?一如既往地有两个相同的选项:
- 记录 class(至少构造函数和析构函数)不是线程安全的。由于它们,调用者有责任确保只有一个线程可以同时访问 A 对象。换句话说,这意味着A只有在单线程程序中才会安全。
- 在 class 自身内部添加同步以使其线程安全。当创建和销毁操作静态成员时,您需要 class 的所有对象的全局互斥锁,换句话说 class 静态对象。
正如@zvone 在评论中注意到的那样,当您删除链中的第一个或最后一个元素时,析构函数可以使 first
和 last
成为悬挂指针。
析构函数(非线程安全版本)应该是:
A::~A() {
if(previous != 0) previous->next=next;
if(next != 0) next->previous=previous;
if (first == this) first = next;
if (last == this) last = previous;
}
线程安全版本可以是:
文件a.h
class A {
public:
static A *first, *last;
A *previous, *next;
static std::mutex mut;
A();
~A();
};
文件a.cpp:
#include "a.h"
A *A::first=0, *A::last=0; // don't put the word static here, that will cause an error
std::mutex A::mut;
A::A() {
mut.lock()
if(first==0) first=this; //first A created
previous=last;
if(previous != 0) previous->next=this;
last=this;
next=0;
mut.unlock();
}
A::~A() {
mut.lock()
if(previous != 0) previous->next=next;
if(next != 0) next->previous=previous;
if (first == this) first = next;
if (last == this) last = previous;
mut.unlock();
}
这里答案的问题是他们没有指出 previous 和 next 是 public 成员的事实 - 这意味着,构造函数和析构函数中的互斥锁没有保护可以使 class 完全线程安全。使这个 class 线程安全的唯一方法是隐藏这些成员并提供访问它们的方法 - 每个内部都有一个同步原语。