class Mutex
{
public:
void Acquire()
void Release()
...
}
为了能使用LockingPtr,你要用你操作系统用到的数据结构和基本函数来实现Mutex。
LockingPtr用受控的变量的类型来作为模板。举例来说,如果你想管理一个Widget,你使用一个LockingPtr<Widget>,这样你可以用一个类型为volatile Widget的变量来初始化它。
LockingPtr的定义非常简单。LockingPtr实现一个相对简单的smart pointer。它目的只是把一个const_cast和一个临界区集中在一起。
Template <typename T>
Class LockingPtr {
Public:
//构造/析构函数
LockingPtr(volatile T&obj, Mutex&mtx)
: pObj_(const_cast<T*>(&obj)),
pMtx_(&mtx)
{ mtx.Lock()}
~LockingPtr()
{ pMtx_->Unlock()}
//模拟指针行为
T&operator*()
{ return *pObj_}
T* operator->()
{ return pObj_}
private:
T* pObj_
Mutex* pMtx_
LockingPtr(const LockingPtr&)
LockingPtr&operator=(const LockingPtr&)
}
尽管简单,LockingPtr对写出正确的多线程代码非常有帮助。你应该把被几个线程共享的对象定义为volatile而且不能对它们使用const_cast——应该始终使用LockingPtr自动对象。我们通过一个例子来说明:
假设你有两个线程共享一个vector<char>对象
class SyncBuf {
public:
void Thread1()
void Thread2()
private:
typedef vector<char>BufT
volatile BufT buffer_
Mutex mtx_//控制对buffer_的访问
}软件开发网
在一个线程函数中,你简单地使用一个LockingPtr<BufT>来取得对buffer_成员变量的受控访问:
void SyncBuf::Thread1() {
LockingPtr<BufT>lpBuf(buffer_, mtx_)
BufT::iterator I = lpBuf->begin()
For (I != lpBuf->end()I) {
...使用*i...
}
}
这些代码既非常容易写也非常容易懂——任何时候你需要用到buffer_,你必须创建一个LockingPtr<BufT>指向它。一旦你这样做,你就能够使用vecotr的所有接口。
非常好的事情是,如果你犯了错,编译器会指出来:
void SyncBuf::Thread2() {
//错误,不能对一个volatile对象调用begin()
BufT::iterator I = buffer_.begin()
//错误!不能对一个volatile对象调用end()
for (I != lpBuf->end()I) {
...使用*i...
}
}
你不能调用buffer_的任何函数,除非你要么使用一个const_cast要么使用LockingPtr。区别是LockingPtr提供了一个有序的途径来对volatile变量使用const_cast。
LockingPtr非常有表现力。如果你只需要调用一个函数,你能够创建一个无名临时LockingPtr对象并直接使用它:
Unsigned int SyncBuf::Size() {
Return LockingPtr<BufT>(buffer_, mtx_)->size()
}
回到基本类型
我们已经看到了volatile保护对象不被不受控制地访问时是多么出色,也看到了LockingPtr提供了多么简单和高效的方法来写线程安全的代码。让我们回到基本类型,那些加了volatile后行为与用户自定类型不同的类型
我们来考虑一个例子,多个线程共享一个类型为int的变量。
Class Count
{
public:
...
void Increment() { ctr_}
void Decrement() { --ctr_}
private:
int ctr_
}
如果Increment和Decrement被不同线程调用,上面的代码片段是有问题的。首先,ctr_必须是volatile,其次,即使象 ctr_那样看上去是原子操作的函数实际上是一个三步操作。内存本身没有算术能力,当递增一个变量时,处理器:
* 读取那个变量到寄存器
* 在寄存器中增加值
* 把结果写回内存
这个三步操作叫做RMW(Read-ModifyWrite 读-改-写)。在执行一个RMW操作的“改”
操作时,为了让其他处理器访问内存,大多数处理器会释放内存总线。
如果那时另一个处理器对同一个变量执行一个RMW操作,我们就有了一个竟态条件;第二个写操作覆盖了第一个的结果。
你也能够用LockingPtr避免这种情况:
class Counter
{
public:
...
void Increment() { *LockingPtr<int>(ctr_, mtx_)}
void Decrement() { --*LockingPtr<int>(ctr_, mtx_)}
private:
volatile int ctr_
Mutex mtx_
}
现在代码正确了,但代码质量比较SyncBuf的代码而言差了很多。为什么?因为在Counter里,如果你错误地直接访问ctr_(没有先对它加锁)编译器不会警告你。如果ctr_是volatile, ctr_也能编译通过,但产生的代码明显是错误的。编译器不再是你的帮手了,只有靠你自己注意才能避免这样的竟态条件。
那你应该怎么做?简单地把你用到的基本数据包装为更高层次的结构,对那些结构用volatile。荒谬的是,尽管本来volatile的用途是用在内建类型上,但实际上直接这样做不是个好主意!
volatile成员函数
到目前为止,我们已经有了包含有volatile数据成员的类,现在我们来考虑设计作为更大对象一部分的类,这些类也被多线程共享。在这里用volatile成员函数有很大帮助。
当设计你的类时,你只对那些线程安全的成员函数加voaltile标识。你必须假定外部代码会用任何代码在任何时刻调用volatile函数。不要忘记:volatile等于可自由用于多线程代码而不用临界区,非volatile等于单线程环境或在一个临界区内。
例如,你定义一个Widget类,实现一个函数的两个变化——一个线程安全的和一个快的,无保护的。
Class Widget
{
public:
void Operation() volatile
void Operation()
...
private:
Mutex mtx_
}
注意用了重载。现在Widget的用户可以用同样的语法来调用Operation,无论你为了获得线程安全调用volatile对象的Operation还是为了获得速度调用常规对象的Operation。但用户必须小心地把被多线程共享的Widget对象定义为volatile。
当实现一个volatile成员函数时,第一个操作通常是对this用一个LockingPtr加锁。剩下的工作可以交给非volatile的对应函数:
软件开发网
void Widget::Operation() volatile
{
LockingPtr<Widget>lpThis(*this, mtx_)
LpThis->Operation()//调用非volatile函数
} http://www.mscto.com
总结
当写多线程程序时,你可以用volatile得到好处。你必须遵守下面的规则:
* 定义所有的被共享的对象为volatile。
* 不要对基本类型直接用volatile
* 当定义可被共享类时,使用volatile成员函数来表示线程安全。
如果你这样做,而且如果你使用那个简单的返型组件LockingPtr,你能够写出线程安
全的代码而不用更多考虑竟态条件,因为编译器能为你留心,会为你主动指出你错误的地方。
我参与的几个使用volatile和LockingPtr的计划获得很好的效果。代码清晰易懂。我记得碰到几处死锁,但我情愿遇到死锁也不要竟态条件,因为死锁调试起来容易得多。事实上没有遇到任何问题是关于竟态条件的。
交叉类型 intersection types是将多个类型合并成一个类型
联合类型表示一个值可以是几种类型之一
使用类型断言,需要多次判断十分麻烦。所以使用类型保护
typeof 只能用于 number , string , boolean , symbol (只有这几种类型会被认为是类型保护)
对于任何类型 T, keyof T的结果为 T上已知的公共属性名的联合
1)首先,使用keyof关键字,它是索引类型查询操作符,它能够获得任何类型T上已知的公共属性名的联合。如例子中,keyof T相当于'name' | 'age'
2)然后,K extends keyof T表明K的取值限制于'name' | 'age'
3)而T[K]则代表对象里相应key的元素的类型
keyof和 T[K]与字符串索引签名进行交互。 如果你有一个带有字符串索引签名的类型,那么 keyof T会是 string。 并且 T[string]为索引签名的类型:
它的语法与索引签名的语法类型,内部使用了 for .. in。 具有三个部分:
我们还可以写出更多的通用映射类型,如:
https://www.ruphi.cn/archives/266/
结论:
(1) this始终等于currentTarget即事件的监听函数所绑定的节点对象
(2) target指的是最初触发事件的监听函数的节点对象
实例:
js继承机制的思想:原型对象的所有属性和方法,都能被实例对象共享。
prototype对象默认有一个constructor属性,默认指向prototype对象所在的构造函数
instanceof返回一个boolean值,表示对象是否为某个构造函数实例
Object.getPrototypeof() 返回参数对象的原型,这是获得原型对象的标准方法
Object.setPrototypeof(a, b)为参数对象设置原型,返回 参数对象
Object.create( ) 以参数对象为原型,返回实例对象
实例对象的 isPrototypeof 方法,用来判断该对象是否是参数对象的原型
首先你写的有问题var object() 这个还真不知道是什么了
var 是javascript的一个基础类型的泛型 可以是string int char 等等 也称作弱类型
但是他不是一个函数 也不能定义成一个函数
一般直接定义 var object = 1var object = " hellow"
还可以是 var object = function double(x){
return 2 * x
}
或者你因该问的是定义函数的三中方式
第一种:这也是最常规的一种
function double(x){
return 2 * x
}
第二种:这种方法使用了Function构造函数,把参数列表和函数体都作为字符串,很不方便,不建议使用。
var double = new Function('x', 'return 2 * x')
第三种:
var double = function(x) { return 2* x}