在阿里云购买CND服务后,能否把某类静态资源在客户端请求的时候限制下载速度?
在阿里云购买CND服务后,能否把某类静态资源在客户端请求的时候限制下载速度?因为需要偷偷加载放在CND上面的静态资源,文件大小10M左右,但是偷偷下载的同时需要保证页面音视频的正常运行,所以想知道在CDN上面有没有这样的设置
我想知道阿里云的CND怎么管理我上次的图片,我自己的服务器是没有保存原图片资源的
RT我想知道阿里云的CND怎么管理我上次的图片,我自己的服务器是没有保存原图片资源的
阿里云服务器CND加速怎么关闭
网站申请了QQ互联登陆,但是每次登陆报错. 502 Bad Gateway ,不知道CND加速在哪,想关闭试试的.
你没有朋友们有这样的情况的,给个解决方案,谢谢你们.
C1X 系列 : 多线程 (N1494)
1. 关于 C1X 标准
C1X 是 C 语言的下一个标准,用于取代现有的 C99 标准。 C1X 是一个非正式名字,该标准仍在制订中,最新的工作草案是 N1494
,发布于 2010 年 6 月。与 C99 相比, C1X 在语言和库上有显著的变化,本文重点分析 N1494 草案中的多线程部分。
2. 呼之欲出的多线程
不瞒你说, C99
标准里面的内存模型仍然是单线程的,即所有代码都运行在一个线程(进程)内。也许,你简直不敢相信这个是真的,因为说不定你每天都与多线程打交道, 使用
_beginthread , CreateThread 或 pthread_create
等不同平台的函数去创建线程。当你担心每个变量被编译器优化时,不得不加上 volatile 修饰符时,或者加上内存栅栏 (memory
barrier) ,都印证了 C99 为单线程的内存模型。
32
位保护模式的出现,催生了多任务操作系统的诞生,随之而来的就是多线程环境。随着多核时代的到来,多线程环境成为程序开发不可逃避的问题。无锁编程,并行
编程等已经成熟的技术在 C 社区里,常常给初学者遥不可及的感觉。在迫切需要多线程的时代,不同厂商和平台都纷纷开发了各自的多线程库,如 POSIX
的 pthreads 和 window 的 winThread
。软件工程师在特定的平台下,使用各自的线程库来开发多线程或并发程序,并非很难,但可移植却成了他们面临的问题。如 pthreads
在默认情况下,互斥锁是非递归的,要使用递归互斥锁,程序库必须支持一些扩展 feature ;而 window 下的锁默认情况下是递归的。
lock-wait-wakup 方式也不尽然相同,这对开发跨平台的多线程程序来说无异于雪上加霜。
放眼 C 语言的后来者,如 Java ,很早就支持了多线程,并且它的内存模型在不断地修改,以适应发挥多线程的优势。 Erlang
作为一种天生的并发编程语言,踏上编程语言的行列,成为并发编程的新星。同时,很多脚本语都内置地支持线程类或结构。 C/C++
作为后来加入到多线程的行列,尽管已经太晚了,但对于 C 语言社区来说,这无疑是最令人兴奋的消息了。
3. C1X 多线程接口与语义
如果你有 pthreads 的开发经验,应该对 C1X threading 不会感到陌生,因为它们在 API
,参数和语义方面都惊人地一致,以致于你学会了 pthreads ,基本就学会了大半个 C1X threading 了。相反,对于
winthread 的开发人员来说,需要换一个角度来看等多线程,特别是 cnd_wait 和 cnd_signal 之间的 time race
关系。好吧,让我们一览目前最新的 C1X threading 编程吧。
3.1 线程管理
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
thrd_create 创建一个新线程,该线程的工作就是执行 func(arg) 调用,程序员需要为线程编写一个函数,函数签名为: thrd_start_t ,即 int (*)(void*) 类型的函数。新创建的线程的标识符存放在 thr 内。
与 pthread_create 函数相比, thrd_create 函数没有线程属性这一参数,并具线程函数的返回值是 int ,而非 pthreads 的 void * 。这一特点与进程的返回值一致,都是使用整数表示一个任务的结束状态。
thrd_t thrd_current(void);
thrd_current 函数返回调用线程的标识符。类似于 pthreads 下的 pthread_self() 函数。
int thrd_detach(thrd_t thr);
thrd_detach 知会操作系统,当该线程结束时,操作系统负责回收该线程所占用的资源。
int thrd_equal(thrd_t thr0, thrd_t thr1);
thrd_equal 用于判断两个线程标识符是否相等(即标识同一线程), thrd_t
是标准约定的类型,可能是一个基础类型,也可能会是结构体,开发人员应该使用 thrd_equal 来判断两者是否相等,不能直接使用 == 。即使
== 在某个平台下表现出来是正确的,但它不是标准的做法,也不可跨平台。
void thrd_exit(int res)
thrd_exit 函数提早结束当前线程, res 是它的退出状态码。这与进程中的 exit 函数类似。
int thrd_join(thrd_t thr, int *res)
thrd_join 将阻塞当前线程,直到线程 thr 结束时才返回。如果 res 非空,那么 res 将保存 thr
线程的结束状态码。如果某一线程内没有调用 thrd_detach 函数将自己设置为 detach 状态,那么当它结束时必须由另外一个线程调用
thrd_join 函数将它留下的僵死状态变为结束,并回收它所占用的系统资源。
void thrd_sleep(const xtime *xt)
thrd_sleep 函数让当前线程中途休眠,直到由 xt 指定的时间过去后才醒过来。
void thrd_yield(void)
thrd_yield 函数让出 CPU 给其它线程或进程。
3.2 互斥对象和函数
C1X threading 中提供了丰富的互斥对象,用户只需 mtx_init 初始化时,指定该互斥对象的类型即可,如递归的,支持 timeout 和,或者支持锁检测。
int mtx_int(mtx_t *mtx, int type);
mtx_init 函数用于初始化互斥对象, type 决定互斥对象的类型,一共有下面 6 种类型:
mtx_plain — 简单的,非递归互斥对象
mtx_timed — 非递归的,支持超时的互斥对象
mtx_try — 非递归的,支持锁检测的互斥对象
mtx_plain | mtx_recursive — 简单的,递归互斥对象
mtx_timed | mtx_recursive — 支持超时的递归互斥对象
mtx_try | mtx_recursive – 支持锁检测的递归互斥对象
int mtx_lock(mtx_t *mtx)
int mtx_timedlock(mtx_t *mtx, const xtime *xt)
int mtx_trylock(mtx_t *mtx)
mtx_xxxlock 函数对 mtx 互斥对象进行加锁 , 它们会阻塞,直到获取锁,或者 xt 指定的时间已过去。而 trylock 版本会进行锁检测,如果该锁已被其它线程占用,那么它马上返回 thrd_busy 。
int mtx_unlock(mtx_t *mtx)
mtx_unlock 对互斥对象 mtx 进行解锁。
3.3 条件变量
C1X 中的条件变量与 pthreads 中的条件变量是一样的, C1X 通过 mtx 对象和条件变量来实现 wait-notify 机制,这与 Java 语言里 Object 对象中的 wait() 和 notify() 方法类似。
int cnd_init(cnd_t *cond)
初始化条件变量,所有条件变量必须初始化后才能使用。
int cnd_wait(cnd_t *cond, mtx_t *mtx)
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
cnd_wait 函数自动对 mtx 互斥对象进行解锁操作,然后阻塞,直到条件变
量 cond 被 cnd_signal 或 cnd_broadcast 调用唤醒,当前线程变为非阻塞时,它将在返回之前锁住 mtx 互斥对
象。 cnd_timedwait 函数与 cnd_wait 类似,例外之处是当前线程在 xt 时间点上还未能被唤醒时,它将返回,此时返回值
为 thrd_timeout 。 cnd_wait 和 cnd_timedwait 函数在被调用前,当前线程必须锁住 mtx 互斥对象。
int cnd_signal(cnd_t *cond)
int cnd_broadcast(cnd_t *cond)
cnd_broadcast 函数用于唤醒那些当前已经阻塞在 cond 条件变量上的所有线程,而 cnd_signal 只唤醒其中之一。
void cnd_destroy(cnd_t *cond)
cnd_destroy函数用于销毁条件变量。
3.4 初始化函数
试想一下,如何在一个多线程同时执行的环境下来初始化一个变量,即著名的延迟初始化单例模式。你可能会使用 DCL 技术。但在 C1X threading 环境下,你可以直接使用 call_once 函来实现。
void call_once(once_flag *flag, void (*func)(void))
call_once 函数使用 flag 来保确 func 只被调用一次。第一个线程使用 flag 去调用 call_once 时,函数
func 会被调用,而接下来的使用相同 flag 来调用的 call_once , func 均不会再次被调用,以保正 func
在多线程环境只被调用一次。
3.5 线程专有数据 (thread-specific data, TSD) 和线程局部数据 (thread-local storage, TLS)
在多线程开发中,并不是所有的同步都需要加锁的,有时巧妙的数据分解也可减少锁的碰撞。每个线程都拥有自己私有数据,使用它可以减少线程间共享数据之间的同步开销。
如果要将一些遗留代码进行线程化,很多函数都使用了全局变量,而在多线程环下,最好的方法可能是将这些全局量变量换成线程私有的全局变量即可。
TSD 和 TLS 就是专门用来处理线程私有数据的。 它的生存周期是整个线程的生存周期,但它在每个线程都有一份拷贝,每个线程只能
read-write-update 属于自己的那份。如果通过指针方式来 read-write-update 其它线程的备份,它的行为是未定义的。
C1X 同时提供了 TSD 和 TLS 特性,而 pthreads 只提供 TSD ,但在 linux 下的 gcc 编译器提供了 TLS
作为扩展特性。 TSD 可认为线程私有内存下的 void * 组数,每个数据项的 key
对应于数组的下标,用于索引功能。当一个新线程创建时,线程的 TSD 区域将所有 key 关联的值设置为 NULL 。 TSD
是通过函数的方式来操作的。 C1X 中 TSD 提供的标准函数如下:
int tss_create(tss_t *key, tss_dtor_t dtor)
void tss_delete(tss_t key)
void *tss_get(tss_t key)
int tss_set(tss_t key, void *val)
tss_create 函数创建一个 key , dtor 为该 key 将要关联 value 的析构函数。当线程退出时,会调用 dtor
函数来释放该 key 关联的 value 所占用的资源,当然,如果退出时 value 值为 NULL , dtor 将不被调用。
tss_delete 函数删除一个 key , tss_get/tss_set 分别获得或设置该 key 所关联的 value 。
通过上述 TSD 来操作线程私有变量的方式,显得相对繁琐 ; C1X 提供了 TLS
方法,可以像一般变量的方式去访问线程私有变量。做法很简单,在声明和定义线程私变量时指定 _Thread_local 存储修饰符即可,关于
_Thread_local , C1X 有如下的描述:
在声明式中,_Thread_local 只能单独使用,或者跟 static 或 extern 一起使用。
在某一区块( block scope) 中声明某一对象,如果声明存储修饰符有 _Thread_local ,那么必须同时有 static 或 extern 。
如果 _Thread_local 出现在一对象的某个声明式中,那么此对象的其余各处声明式都应该有 _Thread_local 存储修饰符。
如果某一对象的声明式中出现 _Thread_local 存储修饰符,那么它有线程储存期( thread storage duration
)。该对象的生命周期为线程的整个执行周期,它在线程出生时创建,并在线程启动时初始化。每个线程均有一份该对象,使用声明时的名字即可引用正在执行当前
表达式的线程所关联的那个对象。
TLS 方式与传统的全局变量或 static 变量的使用方式完全一致,不同的是, TLS 变量在不同的线程上均有各自的一份。线程访问
TLS 时不会产生 data race ,因为不需要任何加锁机制。 TLS 方式需要编译器的支持,对于任何 _Thread_local
变量,编译器要将之编译并生成放到各个线程的 private memory
区域,并且访问这些变量时,都要获得当前线程的信息,从而访问正确的物理对象,当然这一切都是在链接过程早已安排好的。
4. C1X threading 的未来
C1X threading 的整体设计与 pthreads
的惊人地一致,我甚至怀疑它们是出自一人(团队)之手。我最初接触多线程编译中的等待 – 通知原语是从 Java 中 Object 对象里的
wait 和 notify 函数中获得感性认识,然后在工作中将 pthreads 中的 pthread_cond_wait 和
pthread_cond_signal 函数应用于实际工作中,并解决了很多实际问题,如编写线程安全的数据结构。现在最新的
C1Xthreading 标准,线程等待 – 通知的方式与上述两者如同一辙。
与 pthreads 相比, C1X threading 没有了信号量操作,读写锁和自旋锁。显然开发人员可以借用 C1X
threading
中提供的同步机制来实现信号量和读写锁,但要实现自旋锁是比较难,这需要深入了解所在平台和操作系统提供的原语,在此基础上再实现自旋锁。
对于 window 开发者来说, C1X threading 是一个新的标准,里面提供的同步原语与 winthread
相比,显得有点冷清。不过 window 下的开发人员通常都使用 C++ 或 MFC 提供的多线程库来开发,只是对于那些完全使用
C语言来开始跨平台多线程程序的同行来说,只能完全遵循 C1X threading 标准了。
C1X threading 以内存共享模型作为多线程编程模型,提供的同步机制基于锁来实现。将来是否会提供系统级别的,基于消息传递来实现无锁同步。
5. 总结
多线程和多核开发时代已悄悄降临在我们的身边,无论你现在的开发工作是否与并行开发相关, C1X threading 都应该成为你手中的一把利器。 C1X threading 了却你心中很多疑虑,可移植, TSD/TLS 之抉择等。
文章转自 并发编程网-ifeve.com
C1X 系列 : 多线程 (N1494)
感谢林永听投递本文。 校对:方腾飞
1. 关于 C1X 标准
C1X 是 C 语言的下一个标准,用于取代现有的 C99 标准。 C1X 是一个非正式名字,该标准仍在制订中,最新的工作草案是 N1494 ,发布于 2010 年 6 月。与 C99 相比, C1X 在语言和库上有显著的变化,本文重点分析 N1494 草案中的多线程部分。
2. 呼之欲出的多线程
不瞒你说, C99 标准里面的内存模型仍然是单线程的,即所有代码都运行在一个线程(进程)内。也许,你简直不敢相信这个是真的,因为说不定你每天都与多线程打交道, 使用 _beginthread , CreateThread 或 pthread_create 等不同平台的函数去创建线程。当你担心每个变量被编译器优化时,不得不加上 volatile 修饰符时,或者加上内存栅栏 (memory barrier) ,都印证了 C99 为单线程的内存模型。
32 位保护模式的出现,催生了多任务操作系统的诞生,随之而来的就是多线程环境。随着多核时代的到来,多线程环境成为程序开发不可逃避的问题。无锁编程,并行编程等已经成熟的技术在 C 社区里,常常给初学者遥不可及的感觉。在迫切需要多线程的时代,不同厂商和平台都纷纷开发了各自的多线程库,如 POSIX 的 pthreads 和 window 的 winThread 。软件工程师在特定的平台下,使用各自的线程库来开发多线程或并发程序,并非很难,但可移植却成了他们面临的问题。如 pthreads 在默认情况下,互斥锁是非递归的,要使用递归互斥锁,程序库必须支持一些扩展 feature ;而 window 下的锁默认情况下是递归的。 lock-wait-wakup 方式也不尽然相同,这对开发跨平台的多线程程序来说无异于雪上加霜。
放眼 C 语言的后来者,如 Java ,很早就支持了多线程,并且它的内存模型在不断地修改,以适应发挥多线程的优势。 Erlang 作为一种天生的并发编程语言,踏上编程语言的行列,成为并发编程的新星。同时,很多脚本语都内置地支持线程类或结构。 C/C++ 作为后来加入到多线程的行列,尽管已经太晚了,但对于 C 语言社区来说,这无疑是最令人兴奋的消息了。
3. C1X 多线程接口与语义
如果你有 pthreads 的开发经验,应该对 C1X threading 不会感到陌生,因为它们在 API ,参数和语义方面都惊人地一致,以致于你学会了 pthreads ,基本就学会了大半个 C1X threading 了。相反,对于 winthread 的开发人员来说,需要换一个角度来看等多线程,特别是 cnd_wait 和 cnd_signal 之间的 time race 关系。好吧,让我们一览目前最新的 C1X threading 编程吧。
3.1 线程管理
1
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
thrd_create 创建一个新线程,该线程的工作就是执行 func(arg) 调用,程序员需要为线程编写一个函数,函数签名为: thrd_start_t ,即 int (*)(void*) 类型的函数。新创建的线程的标识符存放在 thr 内。
与 pthread_create 函数相比, thrd_create 函数没有线程属性这一参数,并具线程函数的返回值是 int ,而非 pthreads 的 void * 。这一特点与进程的返回值一致,都是使用整数表示一个任务的结束状态。
1
thrd_t thrd_current(void);
thrd_current 函数返回调用线程的标识符。类似于 pthreads 下的 pthread_self() 函数。
1
int thrd_detach(thrd_t thr);
thrd_detach 知会操作系统,当该线程结束时,操作系统负责回收该线程所占用的资源。
1
int thrd_equal(thrd_t thr0, thrd_t thr1);
thrd_equal 用于判断两个线程标识符是否相等(即标识同一线程), thrd_t 是标准约定的类型,可能是一个基础类型,也可能会是结构体,开发人员应该使用 thrd_equal 来判断两者是否相等,不能直接使用 == 。即使 == 在某个平台下表现出来是正确的,但它不是标准的做法,也不可跨平台。
1
void thrd_exit(int res)
thrd_exit 函数提早结束当前线程, res 是它的退出状态码。这与进程中的 exit 函数类似。
1
int thrd_join(thrd_t thr, int *res)
thrd_join 将阻塞当前线程,直到线程 thr 结束时才返回。如果 res 非空,那么 res 将保存 thr 线程的结束状态码。如果某一线程内没有调用 thrd_detach 函数将自己设置为 detach 状态,那么当它结束时必须由另外一个线程调用 thrd_join 函数将它留下的僵死状态变为结束,并回收它所占用的系统资源。
1
void thrd_sleep(const xtime *xt)
thrd_sleep 函数让当前线程中途休眠,直到由 xt 指定的时间过去后才醒过来。
1
void thrd_yield(void)
thrd_yield 函数让出 CPU 给其它线程或进程。
3.2 互斥对象和函数
C1X threading 中提供了丰富的互斥对象,用户只需 mtx_init 初始化时,指定该互斥对象的类型即可,如递归的,支持 timeout 和,或者支持锁检测。
1
int mtx_int(mtx_t *mtx, int type);
mtx_init 函数用于初始化互斥对象, type 决定互斥对象的类型,一共有下面 6 种类型:
mtx_plain — 简单的,非递归互斥对象
mtx_timed — 非递归的,支持超时的互斥对象
mtx_try — 非递归的,支持锁检测的互斥对象
mtx_plain | mtx_recursive — 简单的,递归互斥对象
mtx_timed | mtx_recursive — 支持超时的递归互斥对象
mtx_try | mtx_recursive – 支持锁检测的递归互斥对象
1
int mtx_lock(mtx_t *mtx)
2
3
int mtx_timedlock(mtx_t *mtx, const xtime *xt)
4
5
int mtx_trylock(mtx_t *mtx)
mtx_xxxlock 函数对 mtx 互斥对象进行加锁 , 它们会阻塞,直到获取锁,或者 xt 指定的时间已过去。而 trylock 版本会进行锁检测,如果该锁已被其它线程占用,那么它马上返回 thrd_busy 。
1
int mtx_unlock(mtx_t *mtx)
mtx_unlock 对互斥对象 mtx 进行解锁。
3.3 条件变量
C1X 中的条件变量与 pthreads 中的条件变量是一样的, C1X 通过 mtx 对象和条件变量来实现 wait-notify 机制,这与 Java 语言里 Object 对象中的 wait() 和 notify() 方法类似。
1
int cnd_init(cnd_t *cond)
初始化条件变量,所有条件变量必须初始化后才能使用。
1
int cnd_wait(cnd_t *cond, mtx_t *mtx)
2
3
int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt)
cnd_wait 函数自动对 mtx 互斥对象进行解锁操作,然后阻塞,直到条件变量 cond 被 cnd_signal 或 cnd_broadcast 调用唤醒,当前线程变为非阻塞时,它将在返回之前锁住 mtx 互斥对象。 cnd_timedwait 函数与 cnd_wait 类似,例外之处是当前线程在 xt 时间点上还未能被唤醒时,它将返回,此时返回值为 thrd_timeout 。 cnd_wait 和 cnd_timedwait 函数在被调用前,当前线程必须锁住 mtx 互斥对象。
1
int cnd_signal(cnd_t *cond)
2
int cnd_broadcast(cnd_t *cond)
cnd_broadcast 函数用于唤醒那些当前已经阻塞在 cond 条件变量上的所有线程,而 cnd_signal 只唤醒其中之一。
1
void cnd_destroy(cnd_t *cond)
cnd_destroy函数用于销毁条件变量。
3.4 初始化函数
试想一下,如何在一个多线程同时执行的环境下来初始化一个变量,即著名的延迟初始化单例模式。你可能会使用 DCL 技术。但在 C1X threading 环境下,你可以直接使用 call_once 函来实现。
1
void call_once(once_flag *flag, void (*func)(void))
call_once 函数使用 flag 来保确 func 只被调用一次。第一个线程使用 flag 去调用 call_once 时,函数 func 会被调用,而接下来的使用相同 flag 来调用的 call_once , func 均不会再次被调用,以保正 func 在多线程环境只被调用一次。
3.5 线程专有数据 (thread-specific data, TSD) 和线程局部数据 (thread-local storage, TLS)
在多线程开发中,并不是所有的同步都需要加锁的,有时巧妙的数据分解也可减少锁的碰撞。每个线程都拥有自己私有数据,使用它可以减少线程间共享数据之间的同步开销。
如果要将一些遗留代码进行线程化,很多函数都使用了全局变量,而在多线程环下,最好的方法可能是将这些全局量变量换成线程私有的全局变量即可。
TSD 和 TLS 就是专门用来处理线程私有数据的。 它的生存周期是整个线程的生存周期,但它在每个线程都有一份拷贝,每个线程只能 read-write-update 属于自己的那份。如果通过指针方式来 read-write-update 其它线程的备份,它的行为是未定义的。
C1X 同时提供了 TSD 和 TLS 特性,而 pthreads 只提供 TSD ,但在 linux 下的 gcc 编译器提供了 TLS 作为扩展特性。 TSD 可认为线程私有内存下的 void * 组数,每个数据项的 key 对应于数组的下标,用于索引功能。当一个新线程创建时,线程的 TSD 区域将所有 key 关联的值设置为 NULL 。 TSD 是通过函数的方式来操作的。 C1X 中 TSD 提供的标准函数如下:
1
int tss_create(tss_t *key, tss_dtor_t dtor)
2
void tss_delete(tss_t key)
3
void *tss_get(tss_t key)
4
int tss_set(tss_t key, void *val)
tss_create 函数创建一个 key , dtor 为该 key 将要关联 value 的析构函数。当线程退出时,会调用 dtor 函数来释放该 key 关联的 value 所占用的资源,当然,如果退出时 value 值为 NULL , dtor 将不被调用。 tss_delete 函数删除一个 key , tss_get/tss_set 分别获得或设置该 key 所关联的 value 。
通过上述 TSD 来操作线程私有变量的方式,显得相对繁琐 ; C1X 提供了 TLS 方法,可以像一般变量的方式去访问线程私有变量。做法很简单,在声明和定义线程私变量时指定 _Thread_local 存储修饰符即可,关于 _Thread_local , C1X 有如下的描述:
在声明式中,_Thread_local 只能单独使用,或者跟 static 或 extern 一起使用。
在某一区块( block scope) 中声明某一对象,如果声明存储修饰符有 _Thread_local ,那么必须同时有 static 或 extern 。
如果 _Thread_local 出现在一对象的某个声明式中,那么此对象的其余各处声明式都应该有 _Thread_local 存储修饰符。
如果某一对象的声明式中出现 _Thread_local 存储修饰符,那么它有线程储存期( thread storage duration )。该对象的生命周期为线程的整个执行周期,它在线程出生时创建,并在线程启动时初始化。每个线程均有一份该对象,使用声明时的名字即可引用正在执行当前表达式的线程所关联的那个对象。
TLS 方式与传统的全局变量或 static 变量的使用方式完全一致,不同的是, TLS 变量在不同的线程上均有各自的一份。线程访问 TLS 时不会产生 data race ,因为不需要任何加锁机制。 TLS 方式需要编译器的支持,对于任何 _Thread_local 变量,编译器要将之编译并生成放到各个线程的 private memory 区域,并且访问这些变量时,都要获得当前线程的信息,从而访问正确的物理对象,当然这一切都是在链接过程早已安排好的。
4. C1X threading 的未来
C1X threading 的整体设计与 pthreads 的惊人地一致,我甚至怀疑它们是出自一人(团队)之手。我最初接触多线程编译中的等待 – 通知原语是从 Java 中 Object 对象里的 wait 和 notify 函数中获得感性认识,然后在工作中将 pthreads 中的 pthread_cond_wait 和 pthread_cond_signal 函数应用于实际工作中,并解决了很多实际问题,如编写线程安全的数据结构。现在最新的 C1Xthreading 标准,线程等待 – 通知的方式与上述两者如同一辙。
与 pthreads 相比, C1X threading 没有了信号量操作,读写锁和自旋锁。显然开发人员可以借用 C1X threading 中提供的同步机制来实现信号量和读写锁,但要实现自旋锁是比较难,这需要深入了解所在平台和操作系统提供的原语,在此基础上再实现自旋锁。
对于 window 开发者来说, C1X threading 是一个新的标准,里面提供的同步原语与 winthread 相比,显得有点冷清。不过 window 下的开发人员通常都使用 C++ 或 MFC 提供的多线程库来开发,只是对于那些完全使用 C语言来开始跨平台多线程程序的同行来说,只能完全遵循 C1X threading 标准了。
C1X threading 以内存共享模型作为多线程编程模型,提供的同步机制基于锁来实现。将来是否会提供系统级别的,基于消息传递来实现无锁同步。
5. 总结
多线程和多核开发时代已悄悄降临在我们的身边,无论你现在的开发工作是否与并行开发相关, C1X threading 都应该成为你手中的一把利器。 C1X threading 了却你心中很多疑虑,可移植, TSD/TLS 之抉择等。
CDN加速没有成功
你好!我在阿里云账号上买的CDN加速流量包,CND配置也是正确的,服务器用的不是阿里云的,为什么没有加速成功呢?但是我看CDN管理,流量还是在跑,就是下载速度没有什么变化,只有500K的样子,是哪里没有弄正确吗?
海外cnd对国内是否有效
域名没有备案,主机在法国
由于没有备案我创建cdn加速的时候只能选择"港澳台及
海外"类型,
这样的话国内的请求是就近走香港的节点,还是直接走法国
缓存基础(一)|学习笔记
开发者学堂课程【Redis 入门实战演练: 缓存基础(一)】学习笔记,与课程紧密联系,让用户快速学习知识。课程地址:https://developer.aliyun.com/learning/course/653/detail/10831 缓存基础(一) 内容简介一、缓存概念二、redis 部署与使用 一、缓存概念1.系统缓存(1)buffer 与 cachebuffer:缓冲也叫写缓冲,一般用于写操作,可以将数据先写入内存在写入磁盘,buffer一般用于写缓冲,用于解决不同介质的速度不一致的缓冲,先将数据临时写入到里自己最近的地方,以提高写入速度,CPU 会把数据先写到内存的磁盘缓冲区,然后就认为数据已经写入完成看,然后由内核在后续的时间在写入磁盘,所以服务器突然断电会丢失内存中的部分数据。cache:缓存也叫读缓存,一般用于读操作,CPU 读文件从内存读,如果内存没有就先从硬盘读到内存再读到 CPU,将需要频繁读取的数据放在里自己最近的缓存区域,下次读取的时候即可快速读取。CPU---内存---硬盘 java(2)cache 的保存位置客户端:浏览器缓存服务器---内存(需要程序共享内存分享给 CPU 的存储硬盘)内存:redis、memcached内存:本地服务器、远程服务器硬盘:本机硬盘、远程服务器硬盘将远端服务器的内存共享出来、方便客户端即应用程序的使用(3)cache 的特性(4)凡是缓存都会有一个有效期但部分缓存存在一直有效的情况。例如:公司的 logo 长期以来很小有变动,称为可永久有效的缓存内容可抓取网页的图片或 logo 进行检验例如京东网站购物页面的图片每隔一段时间或节日促销商品像手机、电脑、化妆品的商品图片展示有一定的改变,此时该缓存即为有期限的缓存自动过期:给缓存的数据加上有效时间,超出时间后自动过期删除过期时间:强制过期,源网站更新图片后 CDN 是不会更新的,需要强制是图片缓存过期用户访问---cdn 缓存---原栈缺点:服务器用户访问更新不一致需要cache进行强制更新否则服务器和用户访问二者不能同时更新更新之后,cnd 不会自动更新,那这个时候会让 cnd 上把它缓存的那些图片或者那些开始会让 cnd 把强制更新在cnd的管理台账管理平台。强制更新,必须强制更新,如果不强制更,就算原站更新之后,用户访问时由于他访问 cnd 里面的内容,如果没有 cnd,那么这个用户就会直接发到公司的原网站,那么它可以直接找到实时的最新的信息,但是用了三天之后,这个内容会被缓存到手机上,这个时候怎么强制刷新?可以注册云账号,先买个 cnd 尝试一下他会给管理平台,类似于在创建虚拟机的时候,可以对虚拟机进行删除,创建,重启等操作,对于 cnd 来说,它就可以有一个强制刷新,就可以打开管理界面,然后,把网站缓存内容就提交给 cnd,这样会给C的缓存区域下发的指令,它就会把他 flash 掉,就是删除并全部清空,清空完之后从栈再重新的缓存下来,这样内容就会给更新了 cnd。更新完之后就可以被用户能够访问到了,否则会出现你的网站更新之后,用户访问的还是以前的内容。设置过期时间:在 location 里添加 expires 30d;Location /img {root /data/img;Expires 30d;命中率:即缓存的读取命中率2. 用户层缓存:(1)DNS 缓存默认为60秒,即60秒之内在访问同一个域名就不在进行 DNS 解析:查看 chrome 浏览器的DNS缓存;访问:chrome://net-internals/#dns早期版本可以查找到,新版本出于安全的考虑已经禁止用户查找以免对其进行更改从中可以找到过期时间等等(2)火狐浏览器缓存about:cache包括内存、磁盘、应用缓存到磁盘中的数据包括日期、大小、访问次数、过期时间(3)浏览器缓存过期机制①最后修改时间 系统调用文件的时候会获取文件的最后修改时间,如果没有发生变化就返回给浏览器304的状态码,表示没有发生变化,然后浏览器就使用的本地的缓存展示资源。当跑了个外包服务,但是外部服务里面的那些应程序总是需要提供一些静态资源,或者各种资源程序的各种资源的,也就是我们的用户的浏览器,当我们去访问的时候,先不考虑中间的 CD,先考虑这个用户是直接可以放到我们的外包服务的。并且中间是经过了负载均衡和防火墙,负载机构和防火墙只是负责转发,不负责提供外包的外包的页面。除非是个七层的 nginx。先不考虑中间经过了哪些防火墙和负载均衡,假如是直接通过防火墙到了 web 服务器,那么这个 web 服务器上的内容就是第一次访问的时候,这个内容会直接给用户反馈,因为用户本地没有访问过,也就是本地在之前没有任何缓存,那么在本地没有缓存的情况下会直接访问web服务器并1.jpg 或者是 index.html 等等各种计算资源直接发给用户。当用户上访了,要看个手机,但是考虑之后没买,有可能是这个价格不合适,或者有货比三家,看一下华为的是不是更好一些。所以说他上网只是查看了一下,没有购买,但是看完看完之后一对比可能觉得还是小米的那部手机比较好,还是要购买小米手机就会导致他过了一段时间之后又会去访问小米的网站。假如再一次访问网站,会导致最近一段时间访问过的信息本地存在,缓存情况下去访问了1.jpg 或者是 index.html,但是不知道这个文件是不是过期了,这时我们在访问这个服务器的时候,会把这个最后修改时间处理提交给服务器,有服务器进行判断,判断有没有发生变化,如果服务器没有发生变化,就会给用户一个反馈。一个时间处,告诉你我们网站信息没有什么变化,即返回的状态码。就是用户在访问这个文件时,会附带个稳定的最后修改时间,如果这个最后的修改时间,服务端进行判断没有发生过变化,那么之后服务端就会在这个地方给客户端再返回一个状态码。这个状况说明服务端在这种情况下,在我们的发给客户端访问的文件内容在文件的最后修改时间没有发生变化的时候,会给客户一个304,通过这个304状态码告诉客户端这个文件内容没有发生变化,这个时候用户的浏览器就会使用它之前缓存过的图片、数据,或者直接把它渲染出来,那么就可以看到 web 界面。其实会经么几次交精装交互。第一次是完全的访问,把内容图片先抓取下来,第二次访问的时会先向服务端提个请求,就是要获取文件的时间处到底有没有发生变化,如果没有发生变化就是304码。如果发生这个文件的内容发生了变化,在第一次访问和下一次访问期间,恰好服务端发生过代码部署,或者做了什么操作而导致这个内容发生了变化,那这个发生代码变化就是就是200。服务端判断,客户端提交的文件的最后修改时间不一样,不一样就不会发送304码而是把这个文件的内容重新给客户端发送一次或者再返回一次。
MongoDB数据建模小案例:朋友圈评论内容管理
需求
社交类的APP需求,一般都会引入“朋友圈”功能,这个产品特性有一个非常重要的功能就是评论体系。先整理下需求:
这个APP希望点赞和评论信息都要包含头像信息:
点赞列表,点赞用户的昵称,头像;
评论列表,评论用户的昵称,头像;
数据查询则相对简单:
根据分享ID,批量的查询出10条分享里的所有评论内容;
建模
不好的
跟据上面的内容,先来一个非常非常"朴素"的设计:
{
"_id": 41,
"username": "小白",
"uid": "100000",
"headurl": "http://xxx.yyy.cnd.com/123456ABCDE",
"praise_list": [
"100010",
"100011",
"100012"
],
"praise_ref_obj": {
"100010": {
"username": "小一",
"headurl": "http://xxx.yyy.cnd.com/8087041AAA",
"uid": "100010"
},
"100011": {
"username": "mayun",
"headurl": "http://xxx.yyy.cnd.com/8087041AAB",
"uid": "100011"
},
"100012": {
"username": "penglei",
"headurl": "http://xxx.yyy.cnd.com/809999041AAA",
"uid": "100012"
}
},
"comment_list": [
"100013",
"100014"
],
"comment_ref_obj": {
"100013": {
"username": "小二",
"headurl": "http://xxx.yyy.cnd.com/80232041AAA",
"uid": "100013",
"msg": "good"
},
"100014": {
"username": "小三",
"headurl": "http://xxx.yyy.cnd.com/11117041AAB",
"uid": "100014",
"msg": "bad"
}
}
}
可以看到,comment_ref_obj与praise_ref_obj两个字段,有非常重的关系型数据库痕迹,猜测,这个系统之前应该是放在了普通的关系型数据库上,或者设计者被关系型数据库的影响较深。而在MongoDB这种文档型数据库里,实际上是没有必要这样去设计,这种建模只造成了多于的数据冗余。
另外一个问题是,url占用了非常多的信息空间,这点在压测的时候会有体现,带宽会过早的成为瓶颈。同样username信息也是如此,此类信息相对来说是全局稳定的,基本不会做变化。并且这类信息跟随评论一起在整个APP中流转,也无法结局”用户名修改“的需求。
根据这几个问题,重新做了优化的设计建议。
推荐的设计
{
"_id": 41,
"uid": "100000",
"praise_uid_list": [
"100010",
"100011",
"100012"
],
"comment_msg_list": [
{
"100013": "good"
},
{
"100014": "bad"
}
]
}
对比可以看到,整个结构要小了几个数量级,并且可以发现url,usrname信息都全部不见了。那这样的需求应该如何去实现呢?
从业务抽象上来说,url,username这类信息实际上是非常稳定的,不会发生特别大的频繁变化。并且这两类信息实际上都应该是跟uid绑定的,每个uid含有指定的url,username。是最简单的key,value模型。所以,这类信息非常适合做一层缓存加速读取查询。
进一步的,每个人的好友基本上是有限的,头像,用户名等信息,甚至可以在APP层面进行缓存,也不会消耗移动端过多容量。但是反过来看,每次都到后段去读取,不但浪费了移动端的流量带宽,也加剧了电量消耗。
总结
MongoDB的文档模型固然强大,但绝对不是等同于关系型数据库的粗暴聚合,而是要考虑需求和业务,合理的设计。有些人在设计时,也会被文档模型误导,三七二十一一股脑的把信息塞到一个文档中。反而最后会带来各种使用问题。