跳转到内容
彼岸论坛

小天管理

管理员
  • 内容数

    19567
  • 注册日期

  • 最后上线

  • 得奖次数

    5

小天管理 发表的所有内容

  1. 本人是做 WebRTC 音视频聊天开发,音视频走的都是 UDP 协议。在模拟环境对比腾讯会议和微信,我们做的程序在抗丢包,带宽拥塞控制方面都不比腾讯差多少。 但是在线上环境总有不少用户反馈,使用我们的产品视频通话就卡,使用腾讯会议就没问题。通过统计数据看,用户卡的时候,UDP 通信带宽非常低,基本在 100kbps 以下了。 最近看了 V 站上有讨论国内运营商对 UDP 有限制,所以想弄明白,运营商事对国外到国内的 UDP 有限制,还是境内内部的 UDP 也有限制。为什么相同条件下腾讯会议表现不卡,我们的卡?难道运营商对腾讯会议这些用户量大的软件有白名单?
  2. 最近在找一个简单的 C++11 线程池实现,发现网上有很多相关的代码,在 CSDN 网上看到一个比较简洁的。但是总感觉是不是实现错了。 Any 类 noncopyable 的,仅仅支持移动语义, Result 类使用了 Any 实例作为成员变量,那么 Result 类应该也是 noncopyable 的, Result SubmitTask(std::shared_ptr<Task> taskPtr);直接使用了复制语义,应该是有问题吧,可是代码能够被 vs2022 正常编译。 threadpool.h #pragma once #include <vector> #include <cstdint> #include <queue> #include <memory> #include <atomic> #include <mutex> #include <thread> #include <condition_variable> #include <functional> #include <sstream> #include <unordered_map> // Any 类型:可以接收任意数据的类型 // 任意其他类型 template // 能让一个类型指向其他类型,基类指针可以指向子类 class Any { public: Any() = default; ~Any() = default; Any(const Any&) = delete; Any& operator=(const Any&) = delete; Any(Any&&) = default; Any& operator=(Any&&) = default; template<typename T> Any(T data) : m_base(std::make_unique<Derive<T>>(data)) {} template<typename T> T cast_() { Derive<T>* pd = dynamic_cast<Derive<T>*>(m_base.get()); if (pd == nullptr) { throw "type is unmath!!"; } return pd->m_data; } private: // 基类 class Base { public: virtual ~Base() = default; }; // 派生类 template<typename T> class Derive : public Base { public: Derive(T data) : m_data(data) {} public: T m_data; }; private: std::unique_ptr<Base> m_base; }; // 实现一个信号量类 class Semaphore { public: Semaphore(int limit = 0) : m_resLimit(limit) {} ~Semaphore() = default; // 获取一个信号量资源 void wait() { std::unique_lock<std::mutex> lock(m_mtx); // 如果没有资源,阻塞线程 while (m_resLimit < 1) { m_cond.wait(lock); } m_resLimit--; } // 增加一个信号量资源 void post() { std::unique_lock<std::mutex> lock(m_mtx); m_resLimit++; m_cond.notify_all(); } private: int m_resLimit; // 资源量 std::mutex m_mtx; std::condition_variable m_cond; }; // Task 类型前置声明 class Task; // 实现接收提交到线程池的 task 任务执行完成后的返回值类型 class Result { public: Result(std::shared_ptr<Task> task, bool isValid = true); ~Result() = default; // setVal void setVal(Any result); // get 方法,用户调用这个方法获取 task 的返回值 Any get(); private: Any m_any; Semaphore m_sem; std::shared_ptr<Task> m_task; std::atomic_bool m_isValid; }; // 任务抽象基类 class Task { public: void exec(); void setResult(Result* res); virtual Any run() = 0; private: Result* m_result{ nullptr }; // 不要用智能指针,task 含有 Result Result 含有 task ,可能导致问题 }; class MyTask : public Task { public: MyTask(int start, int end) : m_start(start), m_end(end) {} Any run() { std::ostringstream ostr; ostr << std::this_thread::get_id(); printf("thead %s, task start \n", ostr.str().c_str()); uint64_t sum = 0; for (int i = m_start; i <= m_end; i++) { sum += i; } printf("sum %llu\n", sum); std::this_thread::sleep_for(std::chrono::seconds(2)); printf("thread %s, task finish \n", ostr.str().c_str()); return sum; } private: int m_start; int m_end; }; enum ThreadPoolMode { MODE_FIXED, // 固定数量的线程 MODE_CACHED, // 线程数量可以动态增长 }; class Thread { public: using ThreadFunc = std::function<void(int)>; Thread(ThreadFunc func); ~Thread(); void Start(); int GetId() { return m_threadId; } private: ThreadFunc m_func; static int generateId; int m_threadId; }; class ThreadPool { public: ThreadPool(); ~ThreadPool(); // 设置线程池工作模式 void SetMode(ThreadPoolMode mode); // 设置任务数量上限 void SetTaskQueMaxThreshold(int value); // 给线程池提交任务 Result SubmitTask(std::shared_ptr<Task> taskPtr); // 开启线程池 void Start(int initThreadSize = std::thread::hardware_concurrency()); private: ThreadPool(const ThreadPool&) = delete; ThreadPool& operator=(const ThreadPool&) = delete; // 定义线程函数 void ThreadFunc(int threadId); bool CheckRunningState() const; private: std::unordered_map<int, std::unique_ptr<Thread>> m_threadMap; // 线程列表 int m_initThreadSize; // 初始的线程数量 std::atomic_int m_curThreadSize; // 当前线程数量 std::queue<std::shared_ptr<Task>> m_taskQue; // 任务队列 std::atomic_int m_taskSize; // 任务的数量 int m_taskQueMaxThreshold; // 任务队列的数量上限 std::mutex m_taskQueMtx; // 保证任务队列的线程安全 std::condition_variable m_taskQueNotFullCv; // 表示任务队列不满 std::condition_variable m_taskQueNotEmptyCv; // 表示任务队列不空 std::condition_variable m_exitCv; // 退出线程池 ThreadPoolMode m_poolMode; // 当前线程池的工作模式 std::atomic_bool m_isPoolRuning; // 当前线程工作状态 }; threadpool.cpp #include "threadpool.h" #include <functional> #include <iostream> constexpr int TASK_MAX_THRESHOLD = 1024; ThreadPool::ThreadPool() : m_initThreadSize(4), m_taskSize(0), m_taskQueMaxThreshold(TASK_MAX_THRESHOLD), m_poolMode(ThreadPoolMode::MODE_FIXED) { } ThreadPool::~ThreadPool() { m_isPoolRuning = false; std::unique_lock<std::mutex> lock(m_taskQueMtx); // 线程 要么在阻塞中 要么在工作中 while (m_threadMap.size() > 0) { m_taskQueNotEmptyCv.notify_all(); // 唤醒等待的工作线程 m_exitCv.wait(lock); } } void ThreadPool::SetMode(ThreadPoolMode mode) { if (m_isPoolRuning) { return; } // 线程池启动后,不允许设置线程池一些参数 m_poolMode = mode; } void ThreadPool::SetTaskQueMaxThreshold(int value) { if (m_isPoolRuning) { return; } m_taskQueMaxThreshold = value; } Result ThreadPool::SubmitTask(std::shared_ptr<Task> taskPtr) { // 获取锁 std::unique_lock<std::mutex> lock(m_taskQueMtx); // 线程通信,检查任务队列是否有空余 while (m_taskQue.size() >= m_taskQueMaxThreshold) { // 用于提交任务,不能阻塞太长时间,如果超过 1s ,给用户返回提交失败 if (m_taskQueNotFullCv.wait_for(lock, std::chrono::seconds(1)) == std::cv_status::timeout) { return Result(taskPtr, false); } } // 如果有空余,把任务提交到任务队列中 m_taskQue.emplace(taskPtr); m_taskSize++; // 因为新放了任务,任务队列肯定不为空了,在 m_taskQueNotEmptyCv 进行通知,赶快分配线程执行这个任务 m_taskQueNotEmptyCv.notify_all(); return Result(taskPtr); } void ThreadPool::Start(int initThreadSize) { m_initThreadSize = initThreadSize; m_curThreadSize = initThreadSize; m_isPoolRuning = true; // 创建线程对象 for (int i = 0; i < m_initThreadSize; i++) { auto ptr = std::make_unique<Thread>(std::bind(&ThreadPool::ThreadFunc, this, std::placeholders::_1)); int threadId = ptr->GetId(); m_threadMap.emplace(threadId, std::move(ptr)); } // 启动所有线程 for (auto iter = m_threadMap.cbegin(); iter != m_threadMap.end(); iter++) { iter->second->Start(); } } void ThreadPool::ThreadFunc(int threadId) { while (true) { // 获取锁 std::unique_lock<std::mutex> lock(m_taskQueMtx); std::ostringstream ostr; ostr << std::this_thread::get_id(); printf("thead %s, To Get task \n", ostr.str().c_str()); // 判断任务队列是否为空 while (m_taskQue.empty()) { if (!m_isPoolRuning) { m_threadMap.erase(threadId); m_exitCv.notify_all(); printf("deconstructor thread exit, id = %d\n", threadId); return; } m_taskQueNotEmptyCv.wait(lock); } printf("thead %s, Getted task \n", ostr.str().c_str()); // 不为空,获取任务 auto taskPtr = m_taskQue.front(); // front()返回引用,auto 忽略引用属性,正好满足需要 m_taskQue.pop(); m_taskSize--; lock.unlock(); // 释放锁; // 如果任务队列还有任务,通知其他线程执行任务 if (m_taskQue.size() > 0) { m_taskQueNotEmptyCv.notify_all(); } // 通知队列已经不满 m_taskQueNotFullCv.notify_all(); taskPtr->exec(); if (!m_isPoolRuning) { m_threadMap.erase(threadId); m_exitCv.notify_all(); printf("deconstructor thread exit, id = %d\n", threadId); return; } } } bool ThreadPool::CheckRunningState() const { if (m_isPoolRuning) { return true; } return false; } // 线程方法 int Thread::generateId = 0; Thread::Thread(ThreadFunc func) : m_func(func), m_threadId(generateId++) { } Thread::~Thread() { } void Thread::Start() { std::thread t(m_func, m_threadId); t.detach(); } Result::Result(std::shared_ptr<Task> task, bool isValid) : m_task(task), m_isValid(isValid) { m_task->setResult(this); } void Result::setVal(Any result) { m_any = std::move(result); m_sem.post(); // 通知已经获得结果 } Any Result::get() { if (!m_isValid) { return ""; } m_sem.wait(); // 等待结果 return std::move(m_any); } void Task::exec() { if (m_result != nullptr) { Any result = run(); // 这里发生多态调用 m_result->setVal(std::move(result)); } } void Task::setResult(Result* res) { m_result = res; } main.cpp #include "threadpool.h" #include <chrono> #include <iostream> using std::cout; using std::endl; int main(int argc, char* argv[]) { { ThreadPool pool; pool.Start(4); Result res1 = pool.SubmitTask(std::make_shared<MyTask>(1, 100000000)); Result res2 = pool.SubmitTask(std::make_shared<MyTask>(100000001, 200000000)); Result res3 = pool.SubmitTask(std::make_shared<MyTask>(200000001, 300000000)); //uint64_t sum1 = res1.get().cast_<uint64_t>(); //uint64_t sum2 = res2.get().cast_<uint64_t>(); //uint64_t sum3 = res3.get().cast_<uint64_t>(); //cout << (sum1 + sum2 + sum3) << endl; } cout << "main over" << endl; getchar(); return 0; }
  3. 可见国内的(经济)形势有多差了。
  4. 我创建了一个协作空间,所有者是我自己,我希望将所有权完全转移给另外一个人,然后我退出,应该怎么操作 我研究了下应该无法转移 [协作空间] 的所有权,也无法转移文件夹的所有权,难不成只能转移文档?很不合理啊,而且即便转移文档好像也无法把空间给他,他在这个空间里依然没有权限 或者批量导出,然后再导入新的文件夹下,创建新的 [协作空间] ,这样应该能解决问题,有没有更简单的方式
  5. 看静安 Apple 重新开业新闻有个退休老太太店员 突发奇想似乎这也是个出路 有 Apple Store 员工来现身说法一下么
  6. 先来说说我的需求: 数据可导出冷备份/可自建隐私化(最大的需求,自己的内容怎么也不想让任何第三方窥探) 强大的编辑器(文字格式样式傻瓜式调整、像语雀会员一样可以插入图片视频附件) 我不是很习惯用 markdown 编辑,换来换去,换成了编辑器最强大的群晖 note ! 之前用过 Joplin ,使用的 onedrive 同步,同步挺 6 的,然后电脑版和手机版 APP 都有 有道云笔记主力使用了十年+,后来泄露过一次隐私(有道云笔记的员工可以轻松看到你里面的内容,这个有时间细讲,简单说就是有一次我的同步出问题,找客服过程中,客服不小心说漏嘴了)还有有道云笔记目前免费版限制设备,这个就很逆天! 黑曜石也用过,虽然可以使用插件 webdav 同步,但没有时间和精力逐渐去习惯 印象笔记用过,现在妥妥的广告象!吃相很难看!而且免费版限制多! 语雀的编辑器倒是很合我胃口,但它的设计又不是单纯的笔记,数据还是全权给阿里托管... 思源啦什么的都用过,不尽人意... 最后说说目前群晖 Note 的一个不足吧: 不能创建导图,只能把导图导图贴图
  7. 几个月前联通运维时候上门拍照过,不玩 pcdn ,最近也控制了 nas pt 的上传速率,最近一两月上传了 1t-1.5t 上传。最近发现上传又被限速了,让联通师傅去查,又被怀疑是 pcdn 。反馈无果,联通让我自己把上传加速包取消,不给我恢复速率。求解
  8. 不太想付费, 偶尔听一下月付不划算
  9. 我最近废弃了一个项目,名叫 AI Girl Generator 。这个域名容易让人联想到不适宜内容,而且从用户提示词来看,也有这种暗示。 不过之前的投入并没有白费。原网站现在华丽变身成了 cute-coloringpages.com ,这是一个专门为小朋友设计的涂色页面网站。当然,也有不少成人喜欢这种创作方式,适合各个年龄层的用户。
  10. 集成 ChatGPT, Gemini, Claude 等顶级大语言模型,免费使用! 🎁 感谢在 https://www.producthunt.com/posts/unigpt 投票,获取 20%折扣! ⏰ 限时注册即送 10000 积分! 👉 快来体验 https://unigpt.vip 期待你们的反馈! #AI #UniGPT #ChatGPT #Gemini #Claude
  11. 上一期开通的卡应该都用了 几本书 0 损耗 一张非常值得开通的卡 激活账户随机送 10-100 英镑的股票 支持申请虚拟卡和实体借记卡,全球 AMT 取现 400 英镑免费 上车注意事项: 需要英国手机号(不验证) 需要国外扶墙 国籍选中国,居住地址选英国 需要英国地址证明(可用 WISE 账单)可以 ps : https://www.sejda.com/pdf-editor 至少入金 1 英镑完成账户激活后可获得 10 英镑以上的股票(当然我也有 ) 上车地址: https://www.trading212.com/invite/19A1QHIdTE 详情操作步骤; https://mora.app/planet/adngb-6aaaa-aaaan-qdfhq-cai/0PDY9V34T78GVNMCX7B8ZVBC7D giffgaff 包邮哦
  12. 刚才不小心点到这里了,立刻就报告完成了🤔️是不是举报给管理员……我点的帖子就是一个普通帖子
  13. 如题,诚心发问,除了品牌溢价外有哪些功能是豪车独享的?什么品牌都可以
  14. 经过几天的邮件来往,paddle 账号能正常使用了,之前的项目在验证阶段,上传了身份证和照片之后,告诉我,不支持这种类型的身份证,那看来是不支持大陆的身份证了? Verify your identity Thanks for providing your ID and selfie, but unfortunately we’ve been unable to verify your identity because the ID you provided is not supported. Click the button below to complete identity verification again through our trusted partner, Onfido. Here’s what you’ll need to do in Onfido. 1 Fill your personal information 2 Take a photo of your ID 3 Take a video selfie Verify my identity Got a question? Check out our FAQs below. Here’s a few top tips from the Paddle team! Ensure your document is supported by Onfido, and make sure you take a photo of the original document. When taking your selfie, make sure to remove your glasses, move hair away from your face, remove hats or scarves and sit in front of a plain background in a well-lit room. Cheers, The Paddle team
  15. About the us 长亭科技是国内顶尖的网络信息安全公司之一,专注为企业级用户提供高质量的应用安全防护解决方案。公司现位于朝阳区国家会议中心。 About the role 薪资范围:20k-40k/月 我们的工作包括以下方面,你可能会参与: 日常运维:负责公司内外服务的发布,监控,保证服务的安全,稳定和可靠; 系统开发:通过内部系统为公司内部的用户提供运维相关的自助服务,提高运维效率; 资源管理:管理公司的云资源,包括权限分配,成本控制和应急响应,确保资源的有效、合理利用; 安全合规:通过日常巡检和自动工具发现和解决公司内部的安全和合规问题,保证公司的信息安全; 对内支持:在利用自己的专业知识和经验,向同事提供解决问题的方案和最佳实践。 About you 计算机科学或相关专业本科及以上学历,不限工作年限; 熟悉以下至少两种编程语言:Python/Go/Ruby/Rust ; 对 Linux 操作系统有深入理解,熟悉其内部原理; 对计算机网络有深入了解,熟悉常见协议与标准; 善于接受和使用新技术,愿意学习和掌握新的知识和技能; 具有优秀的故障排查能力,善于使用外部工具,快速、准确地定位问题并寻找解决方案; 具有计算机系统和网络安全的基础知识和良好的安全意识; 能够无障碍地进行英语读写。 Preferred qualifications 有长期使用 Linux 作为日常工作环境的经验; 活跃的 Linux 用户组(LUG)的成员,了解并参与开源社区的活动; 有 Kubernetes 部署和运维经验,了解其内部原理; 有 OpenStack 部署和运维经验,接触过虚拟化和分布式存储相关技术。 Contact us 请发送简历至以下邮箱 RC4(Base64Decode("Td8PiTlaDx4+YOZeGC1/vJBvQc8PRA=="), "welcome_to_chaitin")
  16. 自由行 7 月中下旬出发
  17. 仅限 翻身村 单间 或 一房,短租 2 个月,随时可租,越快越好 wx (base64): d3hpZF83Yjh4ODBiejVyczEyMg== 有没有恰好要退租的兄弟姐妹
  18. MoonBit 更新 支持了错误处理机制 函数返回值类型可以用 Int!String 来标识这个函数正常情况下返回 Int ,错误情况下会抛出类型为 String 的错误值,比如 fn div(x: Int, y: Int) -> Int!String { .. } raise 关键字用于中断当前控制流,直接抛出错误,比如 fn div(x: Int, y: Int) -> Int!String { if y == 0 { raise "divide by 0" } x / y } try { expr0 } catch { pattern1 => expr1; pattern2 => expr2; .. } 表达式可以用于捕获 expr0 中抛出的错误,并对其进行模式匹配来处理,比如下面这个函数调用上面的 div 函数,并在 div 函数抛出错误的时候将错误信息打印,并返回默认值 fn div_with_default(x: Int, y: Int, default: Int) -> Int { try { div(x, y)! } catch { s => { println(s); default } } } 此外,可以用后缀运算符 ! 和 !! 进行错误处理,这些后缀运算符只能应用于函数调用,其中: f(x)! 将调用 f 的过程中发生的错误立即重新抛出,其等价于 try { f(x)! } catch { err => raise err } f(x)!! 则会在 f 发生错误的情况下直接 panic ,其等价于 try { f(x)! } catch { _ => panic() } 函数调用的形式包括方法调用,中缀运算符和管道运算符的调用,比如 fn init { let _ = x.f()!! let _ = (x + y)!! let _ = (x |> f)!! } 最后,对可能会抛出错误的函数如果没有使用上述任何错误处理,那么则会报 unhandled error 的错误 支持 Map 字面量语法: fn init { // 键必须是字面量 let m1 : Map[String, Int] = { "x": 1, "y": 2 } let m2 : Map[Int, String] = { 1: "x", 2: "y" } } IDE 更新 修复了 IDE 在补全过程中 builtin package 中的方法会重复出现两次的 bug 修复了 IDE 中缺少 Byte 相关的补全功能 构建系统更新 添加对 internal 包的支持,这些包被放在名为 internal 的目录中。internal 包只能被以 internal 的父目录为根的包导入。 例如,如果有一个包的路径为 username/hello/x/internal/a,该 internal 包的父目录为 username/hello/x,那么只有包username/hello/x 或其子包(例如 username/hello/x/a)能够导入username/hello/x/internal/a,而username/hello/y则不能导入该包。
  19. 公司规模大概就三十人左右,我现在心里有点坎坷 但是让我没想到的是这公司居然发了太 MacBook Air 给我用
  20. 我小孩 5 岁多,最近幼儿园老师反馈说体检视力 0.8 ,打算周末去医院检查下。 平产看电子产品不多怀疑是看书姿势不规范,小孩比较喜欢看故事图书。 请问下,如果确定是 0.8 ,怎么处理比较好呢,不太想这么早戴眼镜。 另外问下买小孩专用的学习桌行不行,是不是智商税。
  21. 原始表 id a1 a2 a3 b1 b2 001 1 2 3 4 5 002 6 7 8 9 10 003 11 12 13 14 15 期望输出 id a b 001 6 9 002 21 19 003 36 29 其中 6=1+2+3 ,9=4+5 数据转换逻辑: 数据表 5000+字段,100w+行, 新字段名 = 老字段名去除数字, 新字段名的内容 = sum(老字段名的内容) 不知道有没有简单的方法解决这个问题, 感谢感谢~
  22. 我使用的是 BCM94360CS2 这款拆机卡,升级到 macOS 14.5 后按照这个教程调整无线网卡驱动,但是最后都无法驱动成功。 我的 opencore kext 配置如下图 整个 opencore 配置如下链接 https://img2.47lab.cn/uPic/efi.zip 想问问 v2 的大佬们,有用同样的卡在 macOS 14 上驱动成功的吗?我目前不知道怎么调整驱动,
  23. macOS 眼睛感觉没那么累,这玩意是心理作用吗? 公司提供的 windows 机器可以加比较高的内存,想用 windows 了。。。
  24. 电脑主机放行李箱里,带上飞机,以及在路上拖来拖去。 现在开机到登录画面就死机,进安全模式删掉显示驱动就可以正常使用,显卡是 RTX3050 ,重新装上驱动一装完就死机。 一般是什么问题?是显示挂了还是主板的 PCIE 槽坏了,由于主板只有一条槽还无法判断是不是槽问题。
  25. 因为它们都是 oppo 家的,系统也是同一个,但是我目前没有找到它们共有的属性字段,因为怕 oppo 又开一个子品牌,导致业务不能及时适配的问题,所以想过来问一下。 Build.MANUFACTURER Build.BRAND 这两个字段是不可靠的,它们各自为政,都不一样
×
×
  • 创建新的...