在编写的多线程程序中,我遇到了一些性能问题,锁定争用率很高。
我通过在32位无符号整数中包含一些标志来解决这个问题。
目前我只是将临时变量中的值移位,然后以原子方式将其写入。
但我真的不想记住确切的位移量或者标志所在的位置。
所以我一直想知道我是否可以使用uint32_t和带有相同大小的bitflags的结构进行联合,我不能通过结构访问bitflags并原子地将其写为uint32_t?
下面是关于我如何工作的代码。它确实有效,但我不确定是否允许这样做
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
typedef struct atomic_flags {
unsigned int flags1 : 2;
unsigned int flags2 : 2;
unsigned int flags3 : 2;
unsigned int flags4 : 2;
unsigned int flags5 : 8;
unsigned int reserved : 16;
}atomic_flags;
union data {
atomic_flags i;
uint32_t q;
} data;
int main() {
union data test1;
union data test2;
test1.i.flags1 = 1;
test1.i.flags2 = 2;
test1.i.flags3 = 3;
test1.i.flags4 = 2;
test1.i.flags5 = 241;
test1.i.reserved = 1337;
printf("%u\n", test1.q);
__atomic_store_n(&test2.q, test1.q, __ATOMIC_SEQ_CST);
printf("test1 flags1: %u\n", test1.i.flags1);
printf("test1 flags2: %u\n", test1.i.flags2);
printf("test1 flags3: %u\n", test1.i.flags3);
printf("test1 flags4: %u\n", test1.i.flags4);
printf("test1 flags5: %u\n", test1.i.flags5);
printf("test1 reserved: %u\n", test1.i.reserved);
printf("test2 flags1: %u\n", test2.i.flags1);
printf("test2 flags2: %u\n", test2.i.flags2);
printf("test2 flags3: %u\n", test2.i.flags3);
printf("test2 flags4: %u\n", test2.i.flags4);
printf("test2 flags5: %u\n", test2.i.flags5);
printf("test2 reserved: %u\n", test2.i.reserved);
}
或者甚至可能这样?
__atomic_store_n(&test2.i.flags1, 2, __ATOMIC_SEQ_CST);
答案 0 :(得分:1)
这是实施定义。
如果你想让所有的掩盖和移动变得更容易并减少出错的可能性,那么更坚固(但更丑陋)的方式就是让预处理器帮助你:
/*
* widths of the bitfields; these values can be changed independently of anything
* else, provided that the total number of bits does not exceed 32.
*/
#define FLAG_flag1_BITS 2
#define FLAG_flag2_BITS 2
#define FLAG_flag3_BITS 2
#define FLAG_flag4_BITS 2
#define FLAG_flag5_BITS 8
/* Macro evaluating to the number of bits in the named flag */
#define FLAG_BITS(flagname) (FLAG_ ## flagname ## _BITS)
/*
* Positions of the flags in the overall bitmask; these adapt to the flag widths
* above, but a new macro (with the same pattern) will be needed if a bitfield
* is added.
*/
#define FLAG_flag1_SHIFT 0
#define FLAG_flag2_SHIFT (FLAG_flag1_SHIFT + FLAG_flag1_BITS)
#define FLAG_flag3_SHIFT (FLAG_flag2_SHIFT + FLAG_flag2_BITS)
#define FLAG_flag4_SHIFT (FLAG_flag3_SHIFT + FLAG_flag3_BITS)
#define FLAG_flag5_SHIFT (FLAG_flag4_SHIFT + FLAG_flag4_BITS)
/* Macro evaluating to the position of the named flag in the overall bitfield */
#define FLAG_SHIFT(flagname) (FLAG_ ## flagname ## _SHIFT)
/* evaluates to a bitmask for selecting the named flag's bits from a bitfield */
#define FLAG_MASK(flagname) \
((~(((uint32_t) 0xffffffff) << FLAG_BITS(flagname))) << FLAG_SHIFT(flagname))
/* evaluates to a bitfield having the specified flag set to the specified value */
#define FLAG(flagname, v) ((v << FLAG_SHIFT(flagname)) & FLAG_MASK(flagname))
/* macro to set the specified flag in the specified bitfield to the specified value */
#define SET_FLAG(flagname, i, v) \
do { i = (i & ~FLAG_MASK(flagname)) | FLAG(flagname, v); } while (0)
/* macro to retrieve the value of the specified flag from the specified bitfield */
#define GET_FLAG(flagname, i) (((i) & FLAG_MASK(flagname)) >> FLAG_SHIFT(flagname))
/* usage example */
int function(uint32_t bitfield) {
uint32_t v;
SET_FLAG(flag2, bitfield, 1);
v = GET_FLAG(flag5, bitfield);
}
虽然这涉及到一堆巨大的宏,但它主要由第一组驱动,它给出了位域宽度。基本上所有这些都将编译为相同的移位和屏蔽操作,无论如何,因为计算将主要由预处理器和/或编译器执行。实际使用非常简单。