Chromium是一个开源的浏览器项目,它提供了现代Web浏览器的许多功能。Chromium的base库是该项目的核心组件之一,为整个浏览器提供了基础的功能和工具。
Base库包含各种客户端编程所需要的基础模块,例如:进程、线程、内存、时间、文件、字符串、调试等等。然而,实际应用中如何有效利用这些模块是个挑战。例如,Base库的线程模型较复杂,类之间耦合度高,导致简单应用难以直接采用。因此,我们需要从中精选模块并灵活组合。下面,我将分享一些个人实践经验和技巧,本次分享主要以进程和线程为切入口,集中于Windows平台的实践,结合QT框架以及Windows系统本身的特性来进行说明。
注:基于开源代码Base库
Base库中对进程的操作很全面,主要划分为kill、launch、info、metrics四种,分别是终止进程、启动进程、遍历进程、进程性能指标
Base库终止进程可以通过进程可执行文件的名称来进行过滤,终止指定名称对应的所有进程,有三种类型的终止进程方式
可以发现,实际最终都会走到KillProcesses,来看一下源码实现
void Process::Exited(int exit_code) const {
base::debug::GlobalActivityTracker::RecordProcessExitIfEnabled(Pid(),
exit_code);
}
bool Process::Terminate(int exit_code, bool wait) const {
constexpr DWORD kWaitMs = 60 * 1000;
DCHECK(IsValid());
bool result = (::TerminateProcess(Handle(), exit_code) != FALSE);
if (result) {
if (wait && ::WaitForSingleObject(Handle(), kWaitMs) != WAIT_OBJECT_0)
DPLOG(ERROR) << "Error waiting for process exit";
Exited(exit_code);
} else {
if (GetLastError() != ERROR_ACCESS_DENIED)
DPLOG(ERROR) << "Unable to terminate process";
if (::WaitForSingleObject(Handle(), kWaitMs) == WAIT_OBJECT_0) {
DWORD actual_exit;
Exited(::GetExitCodeProcess(Handle(), &actual_exit) ? actual_exit
: exit_code);
result = true;
}
}
return result;
}
bool KillProcesses(const FilePath::StringType& executable_name,
int exit_code,
const ProcessFilter* filter) {
bool result = true;
NamedProcessIterator iter(executable_name, filter);
while (const ProcessEntry* entry = iter.NextProcessEntry()) {
Process process = Process::Open(entry->pid());
if (!process.IsValid()) {
result = false;
continue;
}
result &= process.Terminate(exit_code, true);
}
return result;
}
KillProcesses首先会使用TerminateProcess来关闭进程,然后60秒后会使用debug中的GlobalActivityTracker记录进程退出的详情。
Base库启动进程有多种方式,主要分为两大类:LaunchProcess和GetAppOutput*
base::LaunchProcess
是一个用于启动外部进程的函数。它允许你指定命令行参数、工作目录、环境变量等,并且可以等待进程完成或异步地处理进程输出。
base::GetAppOutput
是一个用于执行外部命令并捕获其标准输出的函数。它通常用于简单地执行命令并获取其输出结果。
根据你的需求,可以选择使用base::LaunchProcess或base::GetAppOutput。如果你需要启动进程并进行后续操作,或者需要更多的控制,应该使用LaunchProcess。如果你只是简单地想要执行一个命令并获取它的输出,GetAppOutput会更方便。
LaunchProcess的基本实现是利用了CreateProcess系列函数来实现进程的启动,其中LaunchOptions则是主要和CreateProcess的各个参数一一对应,注意默认启动进程是不等待进程直接退出,不会卡线程,如果需要同步等待进程退出,则需要将LaunchOptions的wait置为true。
进程启动在windows上面需要额外注意UAC也就是是否管理员权限启动,有以下四种场景
这两种情况,应用利用CreateProcess来拉起进程,本身子进程就会继承当前进程的权限,不需要有额外的操作,正常调用LaunchProcess即可,或者自行封装CreateProcess
bool Util::QtCreateProcess(const QString& program, const QString& arguments) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// 将 QString 转换为 Windows 风格的命令行参数
QString commandLine = program + " " + arguments;
// 创建进程
if (!::CreateProcess(NULL, (LPWSTR)commandLine.toStdWString().c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
return false;
}
// 关闭进程和主线程句柄
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return true;
}
针对这种场景,就是常说的降权运行进程的操作,这种有两种实现方式,一种是利用CreateProcessAsUser传入用户态Token来实现用户权限启动进程,一种是explorer来代理进程启动,两种方法各有优劣
CreateProcessAsUser简略实现如下,可自行适配调整使用
inline constexpr const wchar_t* SLowIntegritySid = L"S-1-16-4096";
inline constexpr const wchar_t* SMediumIntegritySid = L"S-1-16-8192";
inline constexpr const wchar_t* SHighIntegritySid = L"S-1-16-12288";
inline constexpr const wchar_t* SSystemIntegritySid = L"S-1-16-16384";
operator HANDLE() {
/* Get the current process' security token as a starting point, then modify
a duplicate so that it runs with a fixed integrity level. */
if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY, &m_curToken)) {
if (::DuplicateTokenEx(m_curToken, 0, NULL, SecurityImpersonation, TokenPrimary, &m_dupToken)) {
if (AdjustTokenIntegrityLevel(m_dupToken, m_lpSid)) {
return m_dupToken;
}
}
}
return nullptr;
}
static BOOL AdjustTokenIntegrityLevel(HANDLE token, const wchar_t* sid) {
/* Convert the string SID to a SID *, then adjust the token's
privileges. */
BOOL ret{};
PSID psd{};
if (::ConvertStringSidToSid(sid, &psd)) {
TOKEN_MANDATORY_LABEL tml;
ZeroMemory(&tml, sizeof(tml));
tml.Label.Attributes = SE_GROUP_INTEGRITY;
tml.Label.Sid = psd;
ret = ::SetTokenInformation(token, TokenIntegrityLevel, &tml, sizeof(tml));
if (!ret) {
L_ERROR_NOCODE(L"SetTokenInformation return false, err:%d", ::GetLastError());
}
::LocalFree(psd);
}
return ret;
}
CIntegrityLevelToken token(SMediumIntegritySid);
if (!::CreateProcessAsUser(token, NULL, (LPWSTR)commandLine.toStdWString().c_str(), NULL, NULL, FALSE, CREATE_PRESERVE_CODE_AUTHZ_LEVEL, NULL, NULL, &si, &pi)) {
return false;
}
explorer代理实现就比较简单了,使用CreateProcess即可
bool Util::CreateProcessAsUserWithExplore(const QString& program) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(STARTUPINFO);
ZeroMemory(&pi, sizeof(pi));
// 将 QString 转换为 Windows 风格的命令行参数
QString commandLine = "explorer.exe " + program;
if (!::CreateProcess(NULL, (LPWSTR)commandLine.toStdWString().c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
return false;
}
// 关闭进程和主线程句柄
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return true;
}
Base库实现这个操作有专门的函数:LaunchElevatedProcess,具体是通过调用ShellExecuteEx,传入“runas”参数来实现提权运行进程。
以下是SHELLEXECUTEINFO结构体中各个字段的含义:
GetAppOutput中一定会有WaitForSingleObject来等待进程退出,所以会同步卡住线程,一般执行一些简单的命令行或者进程才会使用到,否则建议使用LaunchProcess来进行进程启动会有更精确的控制。有如下的变种函数方便使用
BASE_EXPORT bool GetAppOutput(const CommandLine& cl, std::string* output);
BASE_EXPORT bool GetAppOutputAndError(const CommandLine& cl, std::string* output);
BASE_EXPORT bool GetAppOutputWithExitCode(const CommandLine& cl, std::string* output, int* exit_code);
基本使用示例如下
#include <string>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/process/launch.h"
int main() {
base::CommandLine cl(base::FilePath(FILE_PATH_LITERAL("myapp.exe")));
cl.AppendArg("--arg1");
cl.AppendArg("--arg2");
std::string output;
if (base::GetAppOutput(cl, &output)) {
std::cout << "Command output:<< output<< std::endl;
} else {
std::cerr << "Failed to execute command."<< std::endl;
}
return 0;
}
Base库进行了较为完善的封装,使得遍历进程有这很好的体验,之前终止进程已经有过类似的代码,这里遍历进程主要是通过NamedProcessIterator和ProcessFilter来实现,其中ProcessFilter可以通过派生类来实现自定义的过滤逻辑,而NamedProcessIterator则是通过进程可执行文件名来实现过滤
#include "base/process/process_iterator.h"
#include<iostream>
class MyProcessFilter : public base::ProcessFilter {
public:
bool Includes(const base::ProcessEntry& entry) const override {
// 这里可以添加自定义的筛选逻辑
return true; // 假设我们接受所有进程
}
};
int main() {
base::NamedProcessIterator it("chrome.exe", new MyProcessFilter());
base::ProcessEntry entry;
while (it.NextProcessEntry(&entry)) {
std::cout << "Found process:<< entry.cmd_line().value()<< std::endl;
}
return 0;
}
同样,也可以结合原生实现来封装更加灵活的遍历进程函数,以下是搜索指定进程名并返回其完整路径
base::string16 Util::GetProcessPathAndName(HANDLE process_handle) {
wchar_t path_buffer[MAX_PATH] = { 0 };
DWORD path_length = MAX_PATH;
BOOL ret = QueryFullProcessImageName(process_handle, 0, path_buffer, &path_length);
if (ret && path_length > 0 && path_length < MAX_PATH) {
return base::string16(path_buffer, path_length);
}
else {
return L"";
}
}
base::FilePath Util::GetProcessPathByName(const std::wstring& process_name) {
base::FilePath process_path;
base::win::ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
if (!snapshot.IsValid()) {
return process_path;
}
PROCESSENTRY32 entry = {};
entry.dwSize = sizeof(entry);
if (!Process32First(snapshot.Get(), &entry)) {
return process_path;
}
do {
if (entry.szExeFile == process_name) {
base::win::ScopedHandle process(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, entry.th32ProcessID));
if (process.IsValid()) {
base::string16 path_wstring = GetProcessPathAndName(process.Get());
if (!path_wstring.empty()) {
process_path = base::FilePath::FromUTF16Unsafe(path_wstring);
break;
}
}
}
} while (Process32Next(snapshot.Get(), &entry));
return process_path;
}
这里面使用到ScopedHandle来避免句柄泄露,利用base库FilePath来存放路径。
base库允许使用process_metrics来获取进程的运行性能,结合定时器可以实现对进程进行监控来更好的性能优化。
process_metrics监控各种性能指标,涵盖了:IO、内存、CPU等等,以下是一个简单的使用示例
#include "base/process/process_metrics.h"
#include<iostream>
int main() {
// 获取当前进程的句柄
base::ProcessHandle process_handle = base::GetCurrentProcessHandle();
// 创建一个ProcessMetrics对象
base::ProcessMetrics metrics(process_handle);
// 获取CPU使用率
double cpu_usage = metrics.GetPlatformIndependentCPUUsage();
std::cout << "Current process CPU usage:<< cpu_usage<< "%"<< std::endl;
// 获取内存使用情况
size_t working_set_size = metrics.GetWorkingSetSize();
std::cout << "Current process Working Set Size:<< working_set_size<< " bytes"<< std::endl;
return 0;
}
Base库的线程模型虽然复杂且高度耦合,但许多常规应用实际上并不需要完全理解或使用这个模型。在实际应用中,我们主要利用线程来执行异步任务。为了实现这一点,Base库提供了PostTask方法,它极大地简化了异步任务的调度和执行。
鉴于此,我们将围绕PostTask方法构建一个胶水层,以使Base库的线程模型更易于使用。这个胶水层将封装并抽象出必要的功能,使我们能够在常规应用中更方便地利用Base库的线程模型。
注:这一节需要了解base库的一些基本类实现,可以参考Chromium学习
主要实现的思想是:
1、异步任务会划分为四种类型:UI(主线程)任务、IO任务、后台任务、序列任务。
2、UI任务会通过base::win::MessageWindow来实现主线程的窗口循环,利用PostMessage将任务调度到主线程中执行。
3、IO、后台、序列任务,默认使用base库中base::internal::ThreadPoolImpl的线程池创建对应的taskRunner执行。
4、启用debug\debugging_buildflags.h中ENABLE_LOCATION_SOURCE来实现对异步任务的追踪
5、封装Task来实现对异步任务耗时的监控,主要监控主线程任务是否会导致卡界面
enum class Thread_Type {
TYPE_MAIN_UI,
TYPE_FILE_IO,
TYPE_BACKGROUND
};
// 普通任务丢到线程池,主线程任务丢到主线程创建的窗口循环去处理
// 异步任务接口
void PostTask(Thread_Type type, const base::Location& from_here, base::OnceClosure task);
void PostDelayedTask(Thread_Type type, const base::Location& from_here, base::OnceClosure task, base::TimeDelta delay);
bool PostTaskAndReply(Thread_Type type, const base::Location& from_here, base::OnceClosure task, base::OnceClosure reply);
// 传递参数的回复异步任务
template <template <typename> class CallbackType,
typename TaskReturnType,
typename ReplyArgType,
typename = base::EnableIfIsBaseCallback<CallbackType>>
bool PostTaskAndReplyWithResult(Thread_Type type, const base::Location& from_here,
CallbackType<TaskReturnType()> task,
CallbackType<void(ReplyArgType)> reply);
// 抛顺序执行任务
void PostSequnceTask(const base::Location& from_here, base::OnceClosure task);
void PostSequnceDelayedTask(const base::Location& from_here, base::OnceClosure task, base::TimeDelta delay);
// 单物理线程执行任务
void PostSingleThreadTask(const base::Location& from_here, base::OnceClosure task);
void PostSingleThreadDelayedTask(const base::Location& from_here, base::OnceClosure task, base::TimeDelta delay);
这里以PostTask为例表明是如何实现胶水层逻辑
void ThreadPool::PostTask(Thread_Type type, const base::Location& from_here, base::OnceClosure task) {
PostDelayedTask(type, from_here, std::move(task), base::TimeDelta());
}
void ThreadPool::PostDelayedTask(Thread_Type type, const base::Location& from_here, base::OnceClosure task, base::TimeDelta delay) {
switch (type) {
case Thread_Type::TYPE_MAIN_UI:
PostDelayedUITask(from_here, std::move(WrapTask(type, from_here, std::move(task))), delay);
break;
case Thread_Type::TYPE_FILE_IO:
m_spTaskRunnerFileIO->PostDelayedTask(from_here, std::move(WrapTask(type, from_here, std::move(task))), delay);
break;
case Thread_Type::TYPE_BACKGROUND:
m_spTaskRunnerBackGround->PostDelayedTask(from_here, std::move(WrapTask(type, from_here, std::move(task))), delay);
break;
default:
break;
}
}
void ThreadPool::PostDelayedUITask(const base::Location& from_here, base::OnceClosure task, base::TimeDelta delay) {
std::unique_ptr<base::PendingTask> newTask = std::make_unique<base::PendingTask>(from_here, std::move(task),
base::TimeTicks::FromInternalValue(base::TimeTicks::Now().ToInternalValue() + delay.ToInternalValue()));
m_upMessageUI->ScheduleDelayedWork(std::move(newTask));
}
这里如果使用Qt框架,则无法使用Base库的RunLoop来接管主线程的窗口循环,所以没法直接实现异步任务调度到主线程执行,但是实际使用过程中,由于工作线程的异步任务执行完了后常常需要调度到主线程进行绘制界面,所以这里需要借助base::win::MessageWindow来实现主线程的调度,结合base::PendingTask来进行异步任务的调度
base::win::MessageWindow m_hWindow;
m_hWindow.CreateNamed(base::BindRepeating(&MessageForUI::MessageCallback, base::Unretained(this)), std::wstring(L"WxDecryptMainWindow").c_str());
bool MessageForUI::MessageCallback(UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) {
switch (message) {
case kMsgHaveWork:
HandleWorkMessage();
break;
case WM_TIMER:
if (wparam == reinterpret_cast<UINT_PTR>(this))
HandleTimerMessage();
break;
}
return false;
}
这里对于MessageCallback的回调已经进入了主线程,调度任务的话具体可以参考base库的MessagePumpForUI类实现,分别实现ScheduleWork和ScheduleDelayedWork即可,其中延迟任务可以使用base::DelayedTaskQueue来进行管理,这里实际使用的优先队列来进行对任务的排序,每次消费任务保证消费的是最近需要执行延迟任务
using DelayedTaskQueue = std::priority_queue<base::PendingTask>;
bool PendingTask::operator<(const PendingTask& other) const {
// Since the top of a priority queue is defined as the "greatest" element, we
// need to invert the comparison here. We want the smaller time to be at the
// top of the heap.
if (delayed_run_time < other.delayed_run_time)
return false;
if (delayed_run_time > other.delayed_run_time)
return true;
// If the times happen to match, then we use the sequence number to decide.
// Compare the difference to support integer roll-over.
return (sequence_num - other.sequence_num) > 0;
}
这里初始化主要是线程池、TaskRunner、主线程窗口循环初始化
// 根据CPU核数来初始化线程池最大线程数,最大程度利用机器性能
const int num_cores = base::SysInfo::NumberOfProcessors();
base::ThreadPoolInstance::InitParams initParams(std::max(3, num_cores - 1));
m_spThreadPool = std::make_shared<base::internal::ThreadPoolImpl>("Thread Pool");
// 初始化线程池工作线程观察者,方便监控线程的回收和创建
m_spObserver = std::make_shared<ThreadPoolObserver>();
m_spThreadPool->Start(initParams, std::dynamic_pointer_cast<base::WorkerThreadObserver>(m_spObserver).get());
// 创建IO线程TaskRunner
base::TaskTraits traits{ base::TaskPriority::USER_VISIBLE };
m_spTaskRunnerFileIO = m_spThreadPool->CreateTaskRunner(traits);
// 创建后台线程TaskRunner
base::TaskTraits traitsEffort{ base::TaskPriority::BEST_EFFORT };
m_spTaskRunnerBackGround = m_spThreadPool->CreateTaskRunner(traitsEffort);
// 创建顺序任务Runner,这里抛出的任务会顺序执行,但是并不保证在同一线程
base::TaskTraits traitsUIThread{ base::TaskPriority::BEST_EFFORT };
m_spSeqTaskRunner = m_spThreadPool->CreateSequencedTaskRunner(traitsUIThread);
// 创建单线程任务Runner,这里是实际的物理线程
m_spSingleTaskRunner = m_spThreadPool->CreateSingleThreadTaskRunner(traitsUIThread, base::SingleThreadTaskRunnerThreadMode::DEDICATED);
// 主线程窗口循环初始化
m_upMessageUI = std::make_unique<MessageForUI>();
实现带回复的异步任务,主要参考base\task\post_task.h中的模版实现进行了进一步的封装
template <template <typename> class CallbackType,
typename TaskReturnType,
typename ReplyArgType,
typename = base::EnableIfIsBaseCallback<CallbackType>>
bool ThreadPool::PostTaskAndReplyWithResult(Thread_Type type, const base::Location& from_here,
CallbackType<TaskReturnType()> task,
CallbackType<void(ReplyArgType)> reply) {
auto* result = new std::unique_ptr<TaskReturnType>();
return PostTaskAndReply(type, from_here,
base::BindOnce(&base::internal::ReturnAsParamAdapter<TaskReturnType>, std::move(task),
result),
base::BindOnce(&base::internal::ReplyAdapter<TaskReturnType, ReplyArgType>,
std::move(reply), base::Owned(result)));
}
实际调用的话,需要将task和reply的参数和返回值对应好,例如
int readPool::AsyncTaskWithReturn() {
return 1;
}
void ThreadPool::AsyncReplyWithParam(int i) {
L_TRACE("i = [%d]", i);
}
Pool->PostTaskAndReplyWithResult(Thread_Type::TYPE_MAIN_UI, FROM_HERE,
base::BindOnce(&WxThreadPool::AsyncTaskWithReturn, base::Unretained(Pool)),
base::BindOnce(&WxThreadPool::AsyncReplyWithParam, base::Unretained(Pool)));
实现主线程的Reply的话,需要额外封装一层
bool ThreadPool::PostTaskAndReplyOnMainUI(const base::Location& from_here, base::OnceClosure task, base::OnceClosure reply) {
return PostTaskAndReply(Thread_Type::TYPE_BACKGROUND, from_here, std::move(task), base::BindOnce([](const base::Location& from_here, base::OnceClosure reply) {
Pool->PostTask(Thread_Type::TYPE_MAIN_UI, from_here, std::move(reply));
}, from_here, std::move(reply)));
}
针对异步任务,希望实现全方位的监控,一方面是需要打开debug\debugging_buildflags.h中ENABLE_LOCATION_SOURCE来获取base::Location中更为详尽的信息,另外一方面,通过base::Time来实现任务耗时的精确计算,同样可以在胶水层进行实现
PostDelayedUITask(from_here, std::move(task)), delay);
// 调整为
PostDelayedUITask(from_here, std::move(WrapTask(type, from_here, std::move(task))), delay);
将原有的task包裹一层处理即可
base::OnceClosure WxThreadPool::WrapTask(Thread_Type type, const base::Location& from_here, base::OnceClosure task) {
return base::BindOnce([](Thread_Type from_type, const base::Location& from_here, base::OnceClosure fromtask) {
base::Time start_time = base::Time::Now();
std::move(fromtask).Run();
base::Time end_time = base::Time::Now();
base::TimeDelta time_difference = end_time - start_time;
double microseconds_difference = time_difference.InMicrosecondsF();
L_TRACE("[%s]task location [%s] run cost [%.3f]ms", Util::TaskTypeToString(from_type).c_str(), from_here.ToString().c_str(), microseconds_difference / 1000);
}, type, from_here, std::move(task));
}
包裹task需要注意移动语义,因为base库中异步任务的特殊性,必须使用右值来调用task的Run执行异步任务。
获取当前函数执行的详细堆栈以及同时并行的异步任务,可以使用宏封装好
#define PRINT_DEBUG_INFO() \
do { \
base::debug::StackTrace stack_trace; \
L_TRACE("stack_trace [%s]", stack_trace.ToString().c_str()); \
base::debug::TaskTrace task_trace; \
L_TRACE("task trace [%s]", task_trace.ToString().c_str()); \
} while (0)
在任意异步任务实际执行函数头部调用宏即可。
void TaskCenter::SomeTask() {
// 输出堆栈信息
PRINT_DEBUG_INFO();
}
PostTask(**,&TaskCenter::SomeTask, ***);
base库的单例模式使用较为简单,主要是依赖base::Singleton来实现全局的静态变量,注意这里使用的是懒汉模式,这意味着单例对象只有在第一次使用时才会被创建。使用base::Singleton只需将你的类作为模板参数传递给base::Singleton即可。
使用示例
class MySingleton {
public:
// 获取单例对象的引用
static MySingleton& GetInstance() {
return base::Singleton<MySingleton>::get();
}
// 示例方法
void DoSomething() {
std::cout << "Doing something..."<< std::endl;
}
private:
// 私有构造函数,防止外部构造
MySingleton() {}
// 禁止拷贝和赋值
MySingleton(const MySingleton&) = delete;
MySingleton& operator=(const MySingleton&) = delete;
// 单例的析构函数
friend struct base::DefaultSingletonTraits<MySingleton>;
~MySingleton() {}
};
int main() {
MySingleton& singleton = MySingleton::GetInstance();
singleton.DoSomething();
return 0;
}
配合PostTask,常常需要将类成员变量当做异步任务,这时需要同步传入类实例对象来进行异步任务的调用,base::Bind提供了四种类实例绑定策略来实现各种场景下的异步任务的调用:Unretained、RetainedRef、Owned、Passed
基本的使用示例,仅做参考
#include "base/bind.h"
#include "base/callback.h"
#include "base/run_loop.h"
#include<iostream>
class MyClass {
public:
void MyMethod(const std::string& message) {
std::cout<< message<< std::endl;
}
};
void MyFunction(const std::string& message) {
std::cout<< message<< std::endl;
}
int main() {
MyClass my_class;
std::string message = "Hello, World!";
// 使用Unretained
base::OnceClosure unretained_callback = base::BindOnce(&MyClass::MyMethod, base::Unretained(&my_class), message);
// 使用RetainedRef
base::OnceClosure retained_ref_callback = base::BindOnce(&MyClass::MyMethod, base::RetainedRef(&my_class), message);
// 使用Owned(需要手动管理对象的生命周期)
std::unique_ptr<MyClass> my_class_owned(new MyClass());
base::OnceClosure owned_callback = base::BindOnce(&MyClass::MyMethod, base::Owned(my_class_owned.get()), message);
// 使用Passed
base::OnceClosure passed_callback = base::BindOnce(&MyFunction, base::Passed(std::make_unique<std::string>(message)));
// 执行回调
base::RunLoop run_loop;
run_loop.RunUntilIdle();
return 0;
}
本文以进程和线程两大基础模块来对base库进行一个实践应用,汇总了使用过程中的一些问题供读者参考,希望能起到抛砖引玉的作用。
谢谢各位看到这里,如果有感兴趣的模块或者代码需要攻略,也可以留言,会不定时更新。喜欢可以去github点点赞,再次感谢🙏
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。