- 主题:找工作, 大家看一下, 这样的代码水平如何
值不值一个360 T7级别
真是心寒了
难道 43 岁就真的要窝窝囊囊被人欺负?
居然想给我降 T6
我艹了
有没有公司能接收我这样情况的
人不善于搞人际关系, 不擅长演讲, PPT 这些, 也不擅长面试
年纪虽然不小了, 但刻苦努力程度比一般人高
不为别的, 只为手里攒着活就心理难受
责任心也比一般人高
属于代码控, 很难接受逻辑无发控制, 或者逻辑凌乱
这是我写的一篇关于 RCU 的短文
https://www.cnblogs.com/zylthinking/archive/2013/02/05/2889733.html
43 岁了, 年龄卡人, 本没太大心气了
但实在是逼人, 我就拼着现在价值 200 万的股票不要了
不争包子争口气
各位贵人看得起的帮扶一把
--
修改:zylthinking2 FROM 220.181.41.*
FROM 220.181.41.*
这是一个可以防止多个并发干一件事的锁
能达到的语义是允许其中一个干, 然后可以将干的结果分享给其他的
比如 10 个并发读同一个文件, 就可以让其中一个读, 其他的只取其读到的内容
package goc
import (
"sync"
"sync/atomic"
"unsafe"
)
type cond struct {
uptr unsafe.Pointer
mux sync.Mutex
cond *sync.Cond
}
func newCond() *cond {
cond := &cond{}
cond.cond = sync.NewCond(&cond.mux)
return cond
}
func (this *cond) wait() {
this.mux.Lock()
for this.uptr == nil {
this.cond.Wait()
}
this.mux.Unlock()
}
func (this *cond) wake(uptr unsafe.Pointer) {
this.mux.Lock()
this.uptr = uptr
this.mux.Unlock()
this.cond.Broadcast()
}
type LeadLock struct {
nr int64
cond *cond
holder *cond
mux sync.Mutex
}
func NewLeadLock() *LeadLock {
leadlock := &LeadLock{}
leadlock.cond = newCond()
return leadlock
}
func (this *LeadLock) Lock() unsafe.Pointer {
var uptr unsafe.Pointer
n := atomic.AddInt64(&this.nr, 1)
ptr := (*unsafe.Pointer)(unsafe.Pointer(&this.cond))
if n > 1 {
cond := (*cond)(atomic.LoadPointer(ptr))
cond.wait()
uptr = cond.uptr
} else {
this.mux.Lock()
atomic.StoreInt64(&this.nr, 0)
this.holder = (*cond)(atomic.SwapPointer(ptr, unsafe.Pointer(newCond())))
}
return uptr
}
func (this *LeadLock) Unlock(uptr unsafe.Pointer) {
ptr := unsafe.Pointer(&this.holder)
holder := atomic.SwapPointer((*unsafe.Pointer)(ptr), unsafe.Pointer(nil))
if holder == nil {
return
}
(*cond)(holder).wake(uptr)
this.mux.Unlock()
}
【 在 zylthinking2 的大作中提到: 】
: 值不值一个360 T7级别
: 真是心寒了
: 难道 43 岁就真的要窝窝囊囊被人欺负?
: ...................
--
FROM 220.181.41.*
这是一个自己实现的强弱指针类似实现
特点是本身不分强弱指针, 并且任何指针在任何时间可以将实际对象销毁
如此可以用在线程同步中, 如果循环中获得内部指针发现返回 NULL, 则可作为一个退出标志来用
若得到了这个内部指针, 则可以保证在使用的过程中, 肯定不会被释放
#ifndef my_handle_h
#define my_handle_h
#include <stdint.h>
#include "zyl.h"
typedef struct my_handle {
int ref;
int stack;
intptr_t detached;
void* ptr;
free_t free;
} my_handle;
static inline my_handle* handle_attach(void* ptr, free_t fp_free, const char* func, int line)
{
my_handle* handle = (my_handle *) debug_malloc(sizeof(my_handle), func, line);
if (handle != NULL) {
handle->ref = 1;
handle->stack = 1;
handle->ptr = ptr;
handle->free = fp_free;
handle->detached = 0;
}
return handle;
}
static inline void handle_put(my_handle* handle, const char* func, int line)
{
int n = __sync_sub_and_fetch(&(handle)->stack, 1);
assert1(n >= 0);
if (n == 0) {
void* handle_ptr = (void *) __sync_lock_test_and_set(&(handle)->ptr, NULL);
if ((handle)->free && handle_ptr) {
(handle)->free(handle_ptr);
}
}
}
static inline void* handle_get_with(my_handle* handle, int detach, const char* func, int line)
{
int n = __sync_add_and_fetch(&handle->stack, 1);
int b = __sync_bool_compare_and_swap((void **) &handle->detached, (void *) 0, (void *) (intptr_t) detach);
if (!b) {
handle_put(handle, func, line);
return NULL;
}
void* ptr = handle->ptr;
assert2(n > 1 && ptr != NULL, "%d, %p, caller %d@%s", n, ptr, line, func);
return ptr;
}
static inline int handle_clone(my_handle* handle)
{
int n = __sync_add_and_fetch(&handle->ref, 1);
assert1(n > 0);
return n;
}
static inline int handle_release(my_handle* handle, const char* func, int line)
{
int n = __sync_sub_and_fetch(&handle->ref, 1);
assert2(n >= 0, "%p %d:%d, caller %d@%s", handle, handle->ref, handle->stack, line, func);
if (n > 0) {
return n;
}
// it should be safe to simplely check whether detached equals 0.
// we know __sync_xxx functions have mb() semantics, and
// if some one set handle->detached = 1, it only can be called in
// handle_dettach while still holds a reference of handle.
// ok, now we have seen handle->ref = 0 above, meaning
// we have observed the latter handle_release in handle_dettach.
// then, the mb() assure that
// we have observed the earlier handle_get_with(handle, 1).
if (handle->detached == 0) {
handle_put(handle, func, line);
}
assert1(handle->stack == 0);
my_free(handle);
return n;
}
static inline int handle_dettach(my_handle* handle, const char* func, int line)
{
void* ptr = handle_get_with(handle, 1, func, line);
if (ptr != NULL) {
__sync_sub_and_fetch(&handle->stack, 1);
handle_put(handle, func, line);
}
return handle_release(handle, func, line);
}
#define handle_get(handle) handle_get_with((my_handle *) (handle), 0, __FUNCTION__, __LINE__)
#define handle_clone(handle) handle_clone((my_handle *) handle)
#define handle_attach(ptr, fptr) handle_attach(ptr, fptr, __FUNCTION__, __LINE__)
#define handle_put(h) handle_put((my_handle *) h, __FUNCTION__, __LINE__)
#define handle_release(handle) handle_release((my_handle *) (handle), __FUNCTION__, __LINE__)
#define handle_dettach(handle) handle_dettach((my_handle *) (handle), __FUNCTION__, __LINE__)
#endif
【 在 zylthinking2 的大作中提到: 】
: 值不值一个360 T7级别
: 真是心寒了
: 难道 43 岁就真的要窝窝囊囊被人欺负?
: ...................
--
FROM 220.181.41.*
这是一个我自己发明的无锁链表实现
特点是多写一读情况下, 不考虑 CPU 指令的本身实现, 则是 O(1) 的, 没有任何循环
#ifndef lkf_h
#define lkf_h
typedef struct lkf_node {
struct lkf_node* next;
} lkf_node;
typedef struct lkf_list {
struct lkf_node root;
struct lkf_node** tail;
} lkf_list;
#define LKF_INIT(name) {.root = {NULL}, .tail = &(name.root.next)}
#define LKF_LIST(name) \
struct lkf_list name = LKF_INIT(name)
#define INIT_LKF(name) \
do { \
typeof(name) lkf = name; \
lkf->root.next = NULL; \
lkf->tail = &(lkf->root.next); \
} while (0)
static inline void lkf_node_put(struct lkf_list* list, struct lkf_node* node)
{
node->next = NULL;
struct lkf_node** ptr = __sync_lock_test_and_set(&(list->tail), &(node->next));
*ptr = node;
}
static inline struct lkf_node* lkf_node_get_one(struct lkf_list* list)
{
struct lkf_node* head = __sync_lock_test_and_set(&(list->root.next), NULL);
if (head == NULL) {
return NULL;
}
struct lkf_node* next = head->next;
if (next != NULL) {
list->root.next = next;
head->next = head;
return head;
}
int b = __sync_bool_compare_and_swap(&(list->tail), &(head->next), &(list->root.next));
if (b) {
head->next = head;
return head;
}
list->root.next = head;
return NULL;
}
static inline struct lkf_node* lkf_node_get(struct lkf_list* list)
{
struct lkf_node* ptr = __sync_lock_test_and_set(&(list->root.next), NULL);
if (ptr == NULL) {
return NULL;
}
struct lkf_node** last = __sync_lock_test_and_set(&(list->tail), &(list->root.next));
*last = ptr;
return *last;
}
static inline struct lkf_node* lkf_node_next(struct lkf_node* node)
{
struct lkf_node* ptr = node->next;
if (ptr == NULL || ptr->next == NULL) {
return NULL;
}
if (ptr == node) {
return ptr;
}
node->next = ptr->next;
ptr->next = NULL;
return ptr;
}
#ifdef __linux__
#include <linux/futex.h>
#include <syscall.h>
static inline void lkf_node_put_wake(struct lkf_list* list, struct lkf_node* node)
{
node->next = NULL;
struct lkf_node** ptr = __sync_lock_test_and_set(&(list->tail), &(node->next));
if (ptr == &list->root.next) {
while (-1 == syscall(__NR_futex, ptr, FUTEX_WAKE, 1, NULL, NULL, 0));
}
*ptr = node;
}
static inline struct lkf_node* lkf_node_get_wait(struct lkf_list* list)
{
struct lkf_node* ptr = __sync_lock_test_and_set(&(list->root.next), NULL);
while (ptr == NULL) {
syscall(__NR_futex, &list->root.next, FUTEX_WAIT, NULL, NULL, NULL, 0);
ptr = __sync_lock_test_and_set(&(list->root.next), NULL);
};
struct lkf_node** last = __sync_lock_test_and_set(&(list->tail), &(list->root.next));
*last = ptr;
return *last;
}
#endif
#if 1
typedef struct proc_context {
struct lkf_list list;
int stat;
} proc_context;
static int proc_enter(struct proc_context* ctx, struct lkf_node* node)
{
lkf_node_put(&ctx->list, node);
int n = __sync_bool_compare_and_swap(&ctx->stat, 0, 1);
if (n == 0) {
return -1;
}
return 0;
}
static int proc_leave(struct proc_context* ctx)
{
ctx->stat = 0;
__sync_synchronize();
if (ctx->list.tail == &ctx->list.root.next) {
return 0;
}
int n = __sync_bool_compare_and_swap(&ctx->stat, 0, 1);
if (n == 0) {
return 0;
}
return -1;
}
#endif
#endif
【 在 zylthinking2 的大作中提到: 】
: 值不值一个360 T7级别
: 真是心寒了
: 难道 43 岁就真的要窝窝囊囊被人欺负?
: ...................
--
FROM 220.181.41.*
这是无锁链表的 golang 版本
package goc
import (
"fmt"
"runtime"
"sync/atomic"
"unsafe"
)
type LkfNode struct {
next unsafe.Pointer
Container interface{}
}
func LknInit(node *LkfNode, container interface{}) {
node.Container = container
}
type LkfList struct {
root LkfNode
tail unsafe.Pointer
}
func (this *LkfList) Explain() {
for cur := this.root.next; cur != nil; {
lkfn := (*LkfNode)(cur)
fmt.Printf("%p ->", lkfn)
cur = lkfn.next
}
fmt.Printf("nil\n")
}
func init() {
var node LkfNode
lkf_node_offset = unsafe.Offsetof(node.next)
}
var lkf_node_offset uintptr = 0
func LkfInit(list *LkfList, addr ...interface{}) {
list.root.next = nil
if len(addr) != 0 {
LknInit(&list.root, addr[0])
}
list.tail = unsafe.Pointer(&list.root.next)
}
func LkfNodePut(list *LkfList, node *LkfNode) {
node.next = nil
ptr := atomic.SwapPointer(&list.tail, unsafe.Pointer(&node.next))
*(*unsafe.Pointer)(ptr) = unsafe.Pointer(node)
}
func LkfNodeGet(list *LkfList) *LkfNode {
first := atomic.SwapPointer(&list.root.next, nil)
ptr := (*LkfNode)(first)
if ptr == nil {
return nil
}
last := atomic.SwapPointer(&list.tail, unsafe.Pointer(&list.root.next))
*(*unsafe.Pointer)(last) = first
ptr = (*LkfNode)(unsafe.Pointer(uintptr(last) - lkf_node_offset))
runtime.KeepAlive(last)
return ptr
}
func LkfNodeNext(node *LkfNode) *LkfNode {
ptr := (*LkfNode)(node.next)
if ptr == nil || ptr.next == nil {
return nil
}
if ptr != node {
node.next = ptr.next
}
ptr.next = nil
return ptr
}
type PContext struct {
List LkfList
stat int32
}
func ProcInit(ctx *PContext) {
LkfInit(&ctx.List, ctx)
}
func ProcEnter(ctx *PContext, node *LkfNode) bool {
LkfNodePut(&ctx.List, node)
return atomic.CompareAndSwapInt32(&ctx.stat, 0, 1)
}
func ProcLeave(ctx *PContext) bool {
atomic.StoreInt32(&ctx.stat, 0)
tail := (*unsafe.Pointer)(atomic.LoadPointer(&ctx.List.tail))
if tail == &ctx.List.root.next {
return true
}
return !atomic.CompareAndSwapInt32(&ctx.stat, 0, 1)
}
【 在 zylthinking2 的大作中提到: 】
: 这是一个我自己发明的无锁链表实现
: 特点是多写一读情况下, 不考虑 CPU 指令的本身实现, 则是 O(1) 的, 没有任何循环
:
: ...................
--
FROM 220.181.41.*
这是以上面的 golang 无锁链表为基础实现的公平mutex锁
所谓公平 mutex 锁, 指的是获得锁的顺序与请求锁的顺序一致
package goc
import (
"fmt"
"os"
"runtime"
"sync"
"sync/atomic"
"unsafe"
)
type seq_node struct {
LkfNode
mux sync.Mutex
}
type SeqLock struct {
nr int64
wl LkfList
head *LkfNode
holder *seq_node
mux sync.Mutex
}
func NewSeqLock() *SeqLock {
seqlock := &SeqLock{}
LkfInit(&seqlock.wl)
return seqlock
}
func (this *SeqLock) Lock() {
var node seq_node
LkfNodePut(&this.wl, &node.LkfNode)
n := atomic.AddInt64(&this.nr, 1)
if n > 1 {
node.mux.Lock()
node.mux.Lock()
if this.holder != &node {
fmt.Println("Bugs in SeqLock")
os.Exit(1)
}
} else {
this.mux.Lock()
this.head = LkfNodeGet(&this.wl)
this.holder = this.next()
}
}
func (this *SeqLock) next() *seq_node {
LABEL:
lkfn := LkfNodeNext(this.head)
if lkfn == nil {
runtime.Gosched()
goto LABEL
}
return (*seq_node)(unsafe.Pointer(lkfn))
}
func (this *SeqLock) Unlock() {
n := atomic.AddInt64(&this.nr, -1)
if n == 0 {
this.mux.Unlock()
return
}
// 本批次最后一个 unlock
// 但 nr > 0 说明有其他的 Lock 已经进入 wl
// 获取一个新的批次
// 可以断言批次一定不为空
if this.head == &this.holder.LkfNode {
this.head = LkfNodeGet(&this.wl)
}
this.holder = this.next()
this.holder.mux.Unlock()
}
【 在 zylthinking2 的大作中提到: 】
: 这是无锁链表的 golang 版本
:
: package goc
: ...................
--
FROM 220.181.41.*
https://github.com/zylthinking/codec 这是我用最短路径算法实现的音视频转码器, 我用转码将编码, 解码, 转码三个概念统一成一个; 里面会有一系列 media_unit, 每个接收一种(或一类)格式的输入, 输出一种(或一类)格式的输出; 使用时指定一个源格式, 一个目标格式, 只要存在一个链条将将源, 目标格式连接起来, 就能做到将源格式转换成目标格式
https://github.com/zylthinking/fdset 这是我自己写的网络框架, 本来支持 linux/freebsd/windows 三种平台, 分别使用epoll/kqueue/iocp 机制; 后来不再写 ios/windows 端了, 因此不再维护了, 剩下 linux 版本
https://github.com/zylthinking/hooker 这是我写着玩的一个 inline asm hooker; 可以替换系统函数为自己的实现, 也在部分函数上能够再跳回到原函数执行; 跳转回原函数不完美, 主要是原函数有 当前指令地址偏移量做相对寻址, 则搞不定, 因为我将目标函数前几个指令拷贝到另一个地址上去执行了, 因此, 相对寻址就乱了
https://github.com/zylthinking/godefer-toy 这个也是个玩具, 用汇编实现了 golang 的 defer 机制, 也和编译器有关系; 玩票性质
--
FROM 220.181.41.*
若谈论技术, 那就陷入细节了
不过我可以给你分析一下里面需要解决的问题
1. 你不可能用 channel 简单实现的了
你可以假设一个数据库字段自增操作, 之所以假设数据库, 是因为这是一个昂贵操作。
以下假设都是基于我面临的问题, 所以不用怀疑我下面描述的合理性, 只管接受这些假设及要求即可。
现在有若干个比如 1000 个协程每个都对该字段 +1 并返回自增后的数字, 因为这是一个昂贵操作, 因此现在要求其中只允许一个协程代理全部的操作, 对其 +1000。
剩下的只需要获得操作的结果 N,
然后这 1000 个协程通过合适的逻辑, 分别返回 [N - 999, N] 之间的一个数字, 以保证返回结果和他亲自调用 +1 并返回自增后的结果一样。
这里的要求是在操作过程中, 仍然会时不时有新的协程对这个字段做 +1 操作, 但必须等+1000 结束后才能进行;
那么等 +1000 结束, 可能有 2 群协程, 一群是 +1000 后拿到结果直接返回的, 另一群是 +1000 结束后, 对字段继续 +1 的;
然后还有要求, 第一群需要同时唤醒, 以尽量降低调度延迟; 第二群里面的需要只唤醒一个, 让其代理对第二群里面的所有自增操作; 自然第一群那些唤醒后分行李的协程不能阻塞第二群被唤醒的那个协程
当然, 这里面没有保证这一群必须是 1000 或某个固定数字, 因为有可能再也没有 +1 的协程到来了, 第二群唤醒的那个协程必须保证只代理它在检查第二群那一瞬间看到的那些 +1, 后继的组成第三群, 不能代理
你用 channel 实现试试, 或者除了别用我的办法, 你用你能想到的任何办法试试, 我也好奇还有没有更优实现。
2. 这里面没有 bug, 你若认为有明显竞态, 说明你没有看懂
: 简单一个,SwapPointer不和LoadPointer配对使用就会带来问题
你甚至都没有看到 LoadPointer 在哪里, 或者你没有意识到另一个swap pointer 操作的目标根本没有要读的必要, 简单说, 就是 lock 返回值会告诉caller 是进入了临界区还是单纯分行李, 分行李的不需要 unlock.
unlock 定义的语意是 只要当前锁确实是上锁的, 则解锁, 并不是只有持有锁的才能解锁,golang mutex.unlock 同样是这个表现。 里面的swap pointer 只是为了保证如果已经解锁了, 则不做任何事。
你可以将你的理解及认为的解决方案说出来, 或者将你认为的 nr 有问题到底怎么有问题说出来
【 在 MyWorkLife 的大作中提到: 】
: 不评价你的遭遇
: 但是代码里面问题不少
: 最大的问题是思维方式
: ...................
--
修改:zylthinking2 FROM 223.104.3.*
FROM 220.181.41.*