- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在考虑如何使用尽可能少的 asm 代码来实现信号量(不是二进制)。
在不使用互斥锁的情况下,我还没有成功地思考和编写它,所以这是迄今为止我能做的最好的事情:
全局:
#include <stdlib.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdbool.h>
typedef struct
{
atomic_ullong value;
pthread_mutex_t *lock_op;
bool ready;
} semaphore_t;
typedef struct
{
atomic_ullong value;
pthread_mutex_t lock_op;
bool ready;
} static_semaphore_t;
/* use with static_semaphore_t */
#define SEMAPHORE_INITIALIZER(value) = {value, PTHREAD_MUTEX_INITIALIZER, true}
bool semaphore_init(semaphore_t *semaphore, unsigned long long init_value)
{
if(semaphore->ready) if(!(semaphore->lock_op = \
calloc(1, sizeof(pthread_mutex_t)))) return false;
else pthread_mutex_destroy(semaphore->lock_op);
if(pthread_mutex_init(semaphore->lock_op, NULL))
return false;
semaphore->value = init_value;
semaphore->ready = true;
return true;
}
bool semaphore_wait(semaphore_t *semaphore)
{
if(!semaphore->ready) return false;
pthread_mutex_lock(&(semaphore->lock_op));
while(!semaphore->value) __asm__ __volatile__("nop");
(semaphore->value)--;
pthread_mutex_unlock(&(semaphore->lock_op));
return true;
}
bool semaphore_post(semaphore_t *semaphore)
{
if(!semaphore->ready) return false;
atomic_fetch_add(&(semaphore->value), (unsigned long long) 1);
return true;
}
lock cmpxchg
)?
<bits/sempahore.h>
的 sem_t 结构收录于
<semaphore.h>
在我看来,它被选择了一条非常不同的道路......
typedef union
{
char __size[__SIZEOF_SEM_T];
long int __align;
} sem_t;
最佳答案
请参阅我的最小天真信号量实现的部分内容,它可能有效。它编译并适合 x86。我认为这对于任何 C11 实现来说都是正确的。
IIRC,可以到 implement a counting lock (aka semaphore) with just a single integer ,您可以通过原子操作访问它。该维基百科链接甚至提供了 up
的算法/down
.您不需要单独的互斥锁。如 atomic_ullong
需要一个互斥锁来支持 objective-c PU 上的原子递增/递减,它将包括一个。 (在 32 位 x86 上可能是这种情况,或者实现使用慢 cmpxchg8
而不是快速 lock xadd
。32 位计数器对于您的信号量来说真的太小了吗?因为 64 位原子在 32 位机器上会更慢。)<bits/sempahore.h>
union 定义显然只是一个具有正确大小的不透明 POD 类型,并不表示实际实现。
正如@David Schwartz 所说,除非您是专家,否则为实际使用实现自己的锁定是愚蠢的。不过,这可能是一种有趣的方式来了解原子操作并找出标准实现中的内幕。请仔细注意他的警告,即锁定实现很难测试。您可以使用当前版本的编译器和您选择的编译选项编写适用于您的硬件上的测试用例的代码......ready
boolean 只是完全浪费空间。如果可以正确初始化ready
标志以便函数查看它有意义,然后您可以将其他字段初始化为合理的初始状态。
我注意到您的代码还有一些其他问题:
#define SEMAPHORE_INITIALIZER(value) = {value, PTHREAD_MUTEX_INITIALIZER, true};
static_semaphore_t my_lock = SEMAPHORE_INITIALIZER(1);
// expands to my_lock = = {1, PTHREAD_MUTEX_INITIALIZER, true};;
// you should leave out the = and ; in the macro def so it works like a value
pthread_mutex_t *lock_op
只是愚蠢。使用值,而不是指针。您的大多数锁定功能都使用互斥锁,因此额外的间接级别只会减慢速度。内存和计数器一起在那里会好得多。互斥锁不需要太多空间。
while(!semaphore->value) __asm__ __volatile__("nop");
nop
不会使忙等待循环减少 CPU 密集型。即使使用超线程,它在 x86 上也可能没有区别,因为整个循环体可能仍然适合 4 uop,因此每个时钟的一次迭代中是否存在
nop
的问题在那里与否。
nop
不需要执行单元,所以至少它不会受到伤害。这个自旋循环发生在持有互斥锁的情况下,这看起来很愚蠢。所以第一个服务员将进入这个自旋循环,而之后的服务员将在互斥锁上自旋。
down
只有 12 个 x86 insns , 2 为
up
(包括
ret
s)。 Godbolt 的非 x86 编译器(ARM/ARM64/PPC 的 gcc 4.8)太旧,无法支持 C11
<stdatomic.h>
. (不过,他们确实有 C++
std::atomic
)。所以不幸的是,我无法轻松检查非 x86 上的 asm 输出。
#include <stdatomic.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
typedef struct {
atomic_int val; // int is plenty big. Must be signed, so an extra decrement doesn't make 0 wrap to >= 1
} naive_sem_t;
#if defined(__i386__) || defined(__x86_64__)
#include <immintrin.h>
static inline void spinloop_body(void) { _mm_pause(); } // PAUSE is "rep nop" in asm output
#else
static inline void spinloop_body(void) { }
#endif
void sem_down(naive_sem_t *sem)
{
while (1) {
while (likely(atomic_load_explicit(&(sem->val), memory_order_acquire ) < 1))
spinloop_body(); // wait for a the semaphore to be available
int tmp = atomic_fetch_add_explicit( &(sem->val), -1, memory_order_acq_rel ); // try to take the lock. Might only need mo_acquire
if (likely(tmp >= 1))
break; // we successfully got the lock
else // undo our attempt; another thread's decrement happened first
atomic_fetch_add_explicit( &(sem->val), 1, memory_order_release ); // could be "relaxed", but we're still busy-waiting and want other thread to see this ASAP
}
}
// note the release, not seq_cst. Use a stronger ordering parameter if you want it to be a full barrier.
void sem_up(naive_sem_t *sem) {
atomic_fetch_add_explicit(&(sem->val), 1, memory_order_release);
}
val
没问题暂时太低 ;这只会让其他线程旋转。另请注意
fetch_add
单个原子操作是关键 .它返回旧值,因此我们可以检测何时
val
在 while 循环的加载和 fetch_add 之间被另一个线程占用。 (注意,我们不需要检查
tmp
是否 == 到 while 循环的加载:如果另一个线程
up
在加载和 fetch_add 之间编辑信号量就可以了。这是使用 fetch_add 而不是cmpxchg)。
atomic_load
自旋循环只是对让所有服务员在
val
上进行原子读-修改-写的性能优化。 . (尽管许多服务员试图用 inc 解密然后撤消,但让服务员看到解锁的锁可能非常罕见)。
PAUSE
自旋循环内的指令。这仍然只是一个完全可移植的 C11 实现的玩具示例。
PAUSE
显然有助于避免对内存排序的错误推测,因此在离开自旋循环后 CPU 运行更有效。
pause
与将逻辑 CPU 交给操作系统以运行不同线程不同。也与
memory_order_???
的正确性和选择无关。参数。
sched_yield(2)
,或更可能是
futex
系统调用,见下文)。也许使用 x86
MONITOR
/
MWAIT
对超线程更加友好;我不知道。我从来没有真正实现过锁定自己,我只是在查找其他 insn 时在 x86 insn 引用中看到所有这些东西。
lock xadd
指令工具
fetch_add
(具有顺序一致性语义,因为
lock
ed 指令总是一个完整的内存屏障)。在非 x86 上,只对 fetch_add 使用获取+释放语义,而不是完全顺序一致性可能会允许更高效的代码。我不确定,但只使用
acquire
很可能允许在 ARM64 上使用更高效的代码。我想我们只需要
acquire
on the fetch_add, not acq_rel ,但我不确定。在 x86 上,代码没有任何区别,因为
lock
ed 指令是执行原子读-修改-写的唯一方法,所以即使
relaxed
将与
seq_cst
相同(除了
compile-time reordering 。)
futex(7)
:
NOTES
To reiterate, bare futexes are not intended as an easy-to-use abstraction for end-users. (There is no wrapper function for this system call in glibc.) Implementors are expected to be assembly literate and to have read the sources of the futex user-space library referenced below.
futex
)。
关于C & 低级信号量实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36094115/
所以我目前正在研究 C 中的 POSIX 线程和信号编程。我的讲师使用 sigset(int sigNumber, void* signalHandlerFUnction) 因为他的笔记不是世界上最好
我正在制作一个 C++ 游戏,它要求我将 36 个数字初始化为一个 vector 。你不能用初始化列表初始化一个 vector ,所以我创建了一个 while 循环来更快地初始化它。我想让它把每个数字
我正在尝试让 Python 发送 EOF信号 (Ctrl+D) 通过 Popen() .不幸的是,我找不到任何关于 Popen() 的引用资料。 *nix 类系统上的信号。这里有谁知道如何发送 EOF
我正在尝试让 Python 发送 EOF信号 (Ctrl+D) 通过 Popen() .不幸的是,我找不到任何关于 Popen() 的引用资料。 *nix 类系统上的信号。这里有谁知道如何发送 EOF
我正在学习编码并拥有一个实时的 Django 项目来保持我的动力。在我的 Django 应用程序中,用户留下评论,而其他人则回复所述评论。 每次用户刷新他们的主页时,我都会计算他们是否收到了关于他们之
登录功能中的django信号有什么用?用户已添加到请求 session 表中。那么 Django auth.login 函数中对信号的最后一行调用是什么? @sensitive_post_param
我已经将用户的创建与函数 create_user_profile 连接起来,当我创建我的用户时出现问题,我似乎连接的函数被调用了两次,而 UserProfile 试图被创建两次,女巫触发了一个错误 列
我有一个来自生产者对象处理的硬件的实时数据流。这会连接到一个消费者,该消费者在自己的线程中处理它以保持 gui 响应。 mainwindow::startProcessing(){ QObje
在我的 iPhone 应用程序中,我想提供某种应用程序终止处理程序,该处理程序将在应用程序终止之前执行一些最终工作(删除一些敏感数据)。 我想尽可能多地处理终止情况: 1) 用户终止应用 2) 设备电
我试图了解使用 Angular Signals 的优势。许多解释中都给出了计数示例,但我试图理解的是,与我下面通过变量 myCount 和 myCountDouble 所做的方式相比,以这种方式使用信
我对 dispatch_uid 的用法有疑问为信号。 目前,我通过简单地添加 if not instance.order_reference 来防止信号的多次使用。 .我现在想知道是否dispatch
有时 django 中的信号会被触发两次。在文档中,它说创建(唯一)dispatch_uid 的一个好方法是模块的路径或名称[1] 或任何可哈希对象的 ID[2]。 今天我尝试了这个: import
我有一个用户定义的 shell 项目,我试图在其中实现 cat 命令,但允许用户单击 CTRL-/ 以显示下一个 x 行。我对信号很陌生,所以我认为我在某个地方有一些语法错误...... 主要...
http://codepad.org/rHIKj7Cd (不是全部代码) 我想要完成的任务是, parent 在共享内存中写入一些内容,然后 child 做出相应的 react ,并每五秒写回一些内容
有没有一种方法可以找到 Qt 应用程序中信号/槽连接的总数有人向我推荐 Gamma 射线,但有没有更简单的解决方案? 最佳答案 检查 Qt::UniqueConnection . This is a
我正在实现一个信号/插槽框架,并且到了我希望它是线程安全的地步。我已经从 Boost 邮件列表中获得了很多支持,但由于这与 boost 无关,我将在这里提出我的未决问题。 什么时候信号/槽实现(或任何
在我的代码中,我在循环内创建相同类型的新对象并将信号连接到对象槽。这是我的试用版。 A * a; QList aList; int aCounter = 0; while(aCounter aLis
我知道 UNIX 上的 C 有 signal() 可以在某些操作后调用某些函数。我在 Windows 上需要它。我发现了,它存在什么 from here .但是我不明白如何正确使用它。 我在 UNIX
目前我正在将控制台 C++ 项目移植到 Qt。关于移植,我有一些问题。现在我的项目调整如下我有一个派生自 QWidget 的 Form 类,它使用派生自 QObject 的其他类。 现在请告诉我我是否
在我的 Qt 多线程程序中,我想实现一个基于 QObject 的基类,以便从它派生的每个类都可以使用它的信号和槽(例如抛出错误)。 我实现了 MyQObject : public QObject{..
我是一名优秀的程序员,十分优秀!