Kotlin 协程全局作用域详解
全局作用域是Kotlin协程中最基本但也是最容易误用的作用域。正确理解和使用全局作用域对编写健壮的协程代码至关重要。
1. GlobalScope 基础
1.1 什么是 GlobalScope
// GlobalScope 定义
public object GlobalScope : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = EmptyCoroutineContext
}
// 默认使用 Dispatchers.Default 调度器
// 生命周期与应用程序相同
// 不会自动取消 - 这是最大的风险点
1.2 基本用法
// 基本的 GlobalScope 使用
fun basicGlobalScopeUsage() {
// 启动一个全局协程
val job = GlobalScope.launch {
println("GlobalScope task started on thread: ${Thread.currentThread().name}")
delay(1000)
println("GlobalScope task completed")
}
// 带参数的启动
val namedJob = GlobalScope.launch(CoroutineName("GlobalTask")) {
println("Task name: ${coroutineContext[CoroutineName]?.name}")
// 执行工作
}
// 使用不同的调度器
GlobalScope.launch(Dispatchers.IO) {
// IO 操作
}
GlobalScope.launch(Dispatchers.Main) {
// UI 更新 (在支持UI的平台)
}
}
2. GlobalScope 的特性
2.1 独立生命周期
// GlobalScope 协程独立运行,不受调用者生命周期影响
fun demonstrateIndependentLifecycle() {
println("Main thread: ${Thread.currentThread().name}")
GlobalScope.launch {
println("GlobalScope task started at ${System.currentTimeMillis()}")
repeat(5) { i ->
println("GlobalScope iteration $i at ${System.currentTimeMillis()}")
delay(1000)
}
println("GlobalScope task completed at ${System.currentTimeMillis()}")
}
// 主线程继续执行
Thread.sleep(3000) // 等待3秒
println("Main thread exiting after 3 seconds")
// 即使主线程退出,GlobalScope 协程仍然继续运行
// 在真正的应用程序中,只有当应用退出时才会停止
}
2.2 没有结构化并发
// GlobalScope 不提供结构化并发
fun demonstrateNonStructuredConcurrency() {
val parentJob = GlobalScope.launch {
println("Parent started")
// 子协程1 - 不是真正的"子"协程
val child1 = GlobalScope.launch {
try {
repeat(10) { i ->
delay(500)
println("Child1: iteration $i")
}
} finally {
println("Child1: finally block")
}
}
// 子协程2
val child2 = GlobalScope.launch {
try {
repeat(5) { i ->
delay(1000)
println("Child2: iteration $i")
}
} finally {
println("Child2: finally block")
}
}
delay(1500)
println("Parent cancelling...")
// 取消父协程不会取消"子"协程
cancel()
}
Thread.sleep(4000)
println("Main: parent was cancelled, but children continue running")
// 需要手动取消 child1 和 child2
}
2.3 异常处理特性
// GlobalScope 中的异常处理
fun demonstrateExceptionHandling() {
// 方式1:使用 try-catch 在协程内部
GlobalScope.launch {
try {
throw RuntimeException("Error inside GlobalScope")
} catch (e: Exception) {
println("Caught exception inside coroutine: ${e.message}")
}
}
// 方式2:使用 CoroutineExceptionHandler
val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("GlobalScope caught exception: ${exception.message}")
}
GlobalScope.launch(exceptionHandler) {
throw RuntimeException("Error with handler")
}
// 注意:GlobalScope 中的未捕获异常不会崩溃应用
// 但会被打印到标准错误输出
Thread.sleep(1000)
}
3. GlobalScope 的正确使用场景
3.1 真正的全局任务
// 适合 GlobalScope 的场景:应用程序级别的后台任务
object GlobalTaskManager {
// 场景1:心跳检测 - 需要在应用整个生命周期运行
private var heartbeatJob: Job? = null
fun startHeartbeat() {
heartbeatJob = GlobalScope.launch {
while (isActive) {
try {
sendHeartbeat()
delay(30000) // 每30秒发送一次心跳
} catch (e: Exception) {
// 记录错误,继续尝试
logError("Heartbeat failed", e)
delay(60000) // 失败后等待更长时间
}
}
}
}
fun stopHeartbeat() {
heartbeatJob?.cancel()
heartbeatJob = null
}
private suspend fun sendHeartbeat() {
// 发送心跳到服务器
withContext(Dispatchers.IO) {
// 网络请求
}
}
// 场景2:应用级别的缓存清理
fun scheduleCacheCleanup() {
GlobalScope.launch {
while (true) {
delay(3600000) // 每小时清理一次
cleanExpiredCache()
}
}
}
private suspend fun cleanExpiredCache() {
withContext(Dispatchers.IO) {
// 清理过期缓存
}
}
// 场景3:全局错误报告
fun reportError(error: Throwable, context: Map<String, Any> = emptyMap()) {
GlobalScope.launch(Dispatchers.IO) {
try {
// 异步发送错误报告,不阻塞调用者
sendErrorReport(error, context)
} catch (e: Exception) {
// 静默失败,不影响应用主流程
logError("Failed to send error report", e)
}
}
}
private suspend fun sendErrorReport(error: Throwable, context: Map<String, Any>) {
// 发送到错误报告服务
}
// 场景4:性能监控
fun trackPerformance(event: String, duration: Long) {
GlobalScope.launch(Dispatchers.IO) {
// 异步记录性能数据
logPerformanceMetric(event, duration)
}
}
}
3.2 日志记录和监控
// 日志系统适合使用 GlobalScope
object AppLogger {
private val logScope = CoroutineScope(
Dispatchers.IO +
SupervisorJob() + // 使用 SupervisorJob 防止一个日志失败影响其他
CoroutineExceptionHandler { _, e ->
// 日志系统自身的错误处理
System.err.println("Logger error: $e")
}
)
// 但也可以使用 GlobalScope,因为日志是全局的
fun logGlobal(message: String, level: LogLevel = LogLevel.INFO) {
GlobalScope.launch(Dispatchers.IO) {
try {
// 异步写入日志文件
writeToLogFile("${level.name}: $message")
// 同时发送到远程日志服务
sendToRemoteLogService(message, level)
} catch (e: Exception) {
// 静默失败,不影响应用
System.err.println("Logging failed: $e")
}
}
}
// 更好的做法:创建专门的日志作用域
fun logStructured(message: String, level: LogLevel = LogLevel.INFO) {
logScope.launch {
try {
writeToLogFile("${level.name}: $message")
sendToRemoteLogService(message, level)
} catch (e: Exception) {
System.err.println("Logging failed: $e")
}
}
}
fun cleanup() {
logScope.cancel()
}
enum class LogLevel {
DEBUG, INFO, WARN, ERROR
}
}
3.3 工具函数和扩展
// 工具函数中的 GlobalScope 使用
object AsyncUtils {
// 异步执行并忽略结果
fun executeAsync(block: suspend () -> Unit): Job {
return GlobalScope.launch {
try {
block()
} catch (e: Exception) {
// 工具函数需要处理所有异常
System.err.println("Async execution failed: $e")
}
}
}
// 带超时的异步执行
fun executeWithTimeout(
timeoutMillis: Long,
block: suspend () -> Unit,
onTimeout: () -> Unit = {},
onError: (Throwable) -> Unit = { _ -> }
): Job {
return GlobalScope.launch {
try {
withTimeout(timeoutMillis) {
block()
}
} catch (e: TimeoutCancellationException) {
onTimeout()
} catch (e: Exception) {
onError(e)
}
}
}
// 定期执行任务
fun scheduleAtFixedRate(
initialDelay: Long,
period: Long,
unit: TimeUnit = TimeUnit.MILLISECONDS,
block: suspend () -> Unit
): Job {
return GlobalScope.launch {
delay(unit.toMillis(initialDelay))
while (isActive) {
try {
block()
} catch (e: Exception) {
// 处理异常,但继续执行
System.err.println("Scheduled task failed: $e")
}
delay(unit.toMillis(period))
}
}
}
}
4. GlobalScope 的常见误用和风险
4.1 内存泄漏风险
// ❌ 错误示例:在 Android Activity 中使用 GlobalScope
class LeakyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ❌ 错误:GlobalScope 协程不会随 Activity 销毁而取消
GlobalScope.launch {
// 长时间运行的任务
val data = fetchDataFromNetwork()
// Activity 可能已经被销毁,这里会导致崩溃或内存泄漏
updateUI(data)
}
// ❌ 错误:捕获了 Activity 的引用
GlobalScope.launch {
delay(5000)
// 5秒后,Activity 可能已经被销毁,但这里还在引用它
this@LeakyActivity.title = "Updated Title"
}
}
}
// ✅ 正确示例:使用 lifecycleScope
class SafeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ✅ 正确:使用 lifecycleScope
lifecycleScope.launch {
val data = withContext(Dispatchers.IO) {
fetchDataFromNetwork()
}
// 如果 Activity 已经销毁,这里不会执行
updateUI(data)
}
// ✅ 或者使用 viewLifecycleOwner(对于 Fragment)
// viewLifecycleOwner.lifecycleScope.launch { ... }
}
}
4.2 缺乏取消机制
// ❌ 错误示例:无法正确取消任务
class UserRepository {
fun loadUserData(userId: String, callback: (User) -> Unit) {
// 启动一个 GlobalScope 协程
GlobalScope.launch {
val user = fetchUser(userId)
callback(user)
}
// 问题:无法从外部取消这个任务
}
// 调用方无法取消
fun someOperation() {
loadUserData("123") { user ->
// 处理用户数据
}
// 如果用户很快离开了这个页面,请求仍然会继续
// 并且可能调用回调,导致意外行为
}
}
// ✅ 正确示例:提供取消机制
class SafeUserRepository {
// 方法1:返回 Job 以便取消
fun loadUserData(userId: String, callback: (User) -> Unit): Job {
return GlobalScope.launch {
val user = fetchUser(userId)
callback(user)
}
}
// 方法2:使用 CoroutineScope 参数
fun loadUserData(scope: CoroutineScope, userId: String, callback: (User) -> Unit) {
scope.launch {
val user = fetchUser(userId)
callback(user)
}
}
// 方法3:使用 suspend 函数
suspend fun loadUserData(userId: String): User {
return withContext(Dispatchers.IO) {
fetchUser(userId)
}
}
}
4.3 资源泄漏问题
// ❌ 错误示例:资源未正确释放
class ResourceManager {
private val resources = mutableListOf<Closeable>()
fun processData(data: String) {
GlobalScope.launch {
val resource = acquireExpensiveResource()
resources.add(resource)
try {
// 使用资源
resource.process(data)
} finally {
// 问题:如果协程被取消,finally 块可能不会执行
resource.close()
resources.remove(resource)
}
}
}
// 更大的问题:如果忘记调用 cleanup,资源永远不会释放
fun cleanup() {
resources.forEach { it.close() }
resources.clear()
}
}
// ✅ 正确示例:使用结构化并发确保资源释放
class SafeResourceManager {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private val resources = ConcurrentHashMap<Job, Closeable>()
fun processData(data: String): Job {
return scope.launch {
val resource = acquireExpensiveResource()
resources[coroutineContext[Job]!!] = resource
try {
resource.process(data)
} finally {
// 使用 use 块确保资源关闭
resource.use {
resources.remove(coroutineContext[Job])
}
}
}
}
// 自动清理
fun cleanup() {
scope.cancel()
resources.values.forEach { it.close() }
resources.clear()
}
}
5. GlobalScope 的最佳实践
5.1 创建安全的全局作用域包装器
// 创建安全的全局作用域替代品
object SafeGlobalScope {
// 使用 SupervisorJob 防止一个协程失败影响其他
private val supervisorJob = SupervisorJob()
// 添加异常处理器
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
System.err.println("SafeGlobalScope caught exception: $throwable")
// 可以在这里添加错误报告
}
// 安全的作用域
private val safeScope = CoroutineScope(
Dispatchers.Default +
supervisorJob +
exceptionHandler +
CoroutineName("SafeGlobalScope")
)
// 安全的启动方法
fun launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
return safeScope.launch(context, start, block)
}
// 特定类型的任务
fun launchIO(block: suspend CoroutineScope.() -> Unit): Job {
return launch(Dispatchers.IO, block = block)
}
fun launchMain(block: suspend CoroutineScope.() -> Unit): Job {
return launch(Dispatchers.Main, block = block)
}
// 带生命周期的启动(模拟)
fun launchWithLifecycle(
lifecycleCheck: () -> Boolean,
block: suspend CoroutineScope.() -> Unit
): Job {
return launch {
while (lifecycleCheck() && isActive) {
try {
block()
} catch (e: Exception) {
if (lifecycleCheck()) {
// 只有生命周期有效时才处理异常
throw e
}
// 否则静默取消
cancel()
}
}
}
}
// 清理所有协程
fun cancelAll() {
supervisorJob.cancelChildren()
}
// 完全关闭
fun shutdown() {
safeScope.cancel()
}
}
// 使用示例
fun demonstrateSafeGlobalScope() {
// 启动任务
val job1 = SafeGlobalScope.launchIO {
// IO 操作
}
val job2 = SafeGlobalScope.launch {
// 默认调度器的操作
}
// 带生命周期检查
var isActive = true
val job3 = SafeGlobalScope.launchWithLifecycle({ isActive }) {
// 只在 isActive 为 true 时运行
}
// 稍后取消所有任务
Thread.sleep(5000)
SafeGlobalScope.cancelAll()
// 或者取消特定任务
job1.cancel()
}
5.2 限制 GlobalScope 的使用
// 通过代码审查和静态分析限制 GlobalScope 使用
object GlobalScopePolicy {
// 允许使用 GlobalScope 的特定场景
enum class AllowedScenario {
APPLICATION_STARTUP, // 应用启动时
BACKGROUND_SERVICE, // 后台服务
LOGGING, // 日志记录
ANALYTICS, // 分析统计
GLOBAL_EVENT_HANDLER // 全局事件处理
}
// 记录 GlobalScope 的使用
private val usageLog = ConcurrentHashMap<String, AllowedScenario>()
// 批准的启动方法
fun launch(
scenario: AllowedScenario,
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> Unit
): Job {
// 记录使用情况
val stackTrace = Thread.currentThread().stackTrace
val caller = stackTrace.getOrNull(2)?.toString() ?: "unknown"
usageLog[caller] = scenario
// 添加监控信息
val monitoredContext = context + CoroutineName("GlobalScope-$scenario")
return GlobalScope.launch(monitoredContext) {
try {
block()
} catch (e: Exception) {
handleException(scenario, e)
throw e
}
}
}
private fun handleException(scenario: AllowedScenario, exception: Throwable) {
// 根据场景处理异常
when (scenario) {
AllowedScenario.LOGGING -> {
// 日志系统的异常静默处理
System.err.println("Logging exception: $exception")
}
AllowedScenario.ANALYTICS -> {
// 分析系统的异常可以忽略
// 不记录到错误报告
}
else -> {
// 其他场景记录错误
System.err.println("GlobalScope exception in $scenario: $exception")
}
}
}
// 获取使用报告
fun getUsageReport(): Map<String, AllowedScenario> {
return usageLog.toMap()
}
// 清理不再需要的任务
fun cleanup() {
// 可以在这里实现清理逻辑
}
}
// 使用批准的 GlobalScope
fun demonstratePolicy() {
// 正确的使用方式
GlobalScopePolicy.launch(GlobalScopePolicy.AllowedScenario.LOGGING) {
// 日志操作
}
// 需要说明使用场景
GlobalScopePolicy.launch(GlobalScopePolicy.AllowedScenario.ANALYTICS) {
// 发送分析事件
}
}
5.3 监控和调试 GlobalScope
// 监控 GlobalScope 协程的工具
object GlobalScopeMonitor {
private val activeJobs = ConcurrentHashMap<Job, JobInfo>()
data class JobInfo(
val name: String?,
val startTime: Long,
val thread: String,
val stackTrace: List<StackTraceElement>
)
// 包装 GlobalScope.launch 以进行监控
fun monitoredLaunch(
name: String? = null,
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> Unit
): Job {
val jobName = name ?: "GlobalScope-${System.currentTimeMillis()}"
val monitoredContext = context + CoroutineName(jobName)
val job = GlobalScope.launch(monitoredContext) {
// 记录开始
val jobInfo = JobInfo(
name = jobName,
startTime = System.currentTimeMillis(),
thread = Thread.currentThread().name,
stackTrace = Thread.currentThread().stackTrace.toList()
)
activeJobs[coroutineContext[Job]!!] = jobInfo
try {
block()
} finally {
// 记录结束
activeJobs.remove(coroutineContext[Job])
}
}
// 设置完成回调
job.invokeOnCompletion { cause ->
val duration = System.currentTimeMillis() -
(activeJobs[job]?.startTime ?: System.currentTimeMillis())
if (cause != null) {
println("Job '$jobName' failed after ${duration}ms: $cause")
} else {
println("Job '$jobName' completed successfully after ${duration}ms")
}
activeJobs.remove(job)
}
return job
}
// 获取活跃的 GlobalScope 任务
fun getActiveJobs(): List<JobInfo> {
return activeJobs.values.toList()
}
// 检查是否有长时间运行的任务
fun checkForLongRunningTasks(timeoutMillis: Long = 60000): List<JobInfo> {
val now = System.currentTimeMillis()
return activeJobs.values.filter { jobInfo ->
now - jobInfo.startTime > timeoutMillis
}
}
// 取消所有监控的任务
fun cancelAll() {
activeJobs.keys.forEach { it.cancel() }
activeJobs.clear()
}
// 生成监控报告
fun generateReport(): String {
val active = activeJobs.values
val builder = StringBuilder()
builder.appendLine("=== GlobalScope Monitor Report ===")
builder.appendLine("Active jobs: ${active.size}")
active.forEach { jobInfo ->
builder.appendLine("\nJob: ${jobInfo.name}")
builder.appendLine("Running for: ${System.currentTimeMillis() - jobInfo.startTime}ms")
builder.appendLine("Thread: ${jobInfo.thread}")
builder.appendLine("Created from:")
jobInfo.stackTrace.take(5).forEach { element ->
builder.appendLine(" $element")
}
}
return builder.toString()
}
}
// 使用监控的 GlobalScope
fun demonstrateMonitoring() {
// 使用监控版本
val job1 = GlobalScopeMonitor.monitoredLaunch("BackgroundTask") {
delay(30000)
}
val job2 = GlobalScopeMonitor.monitoredLaunch {
// 未命名的任务
delay(60000)
}
// 检查报告
Thread.sleep(5000)
println(GlobalScopeMonitor.generateReport())
// 检查长时间运行的任务
val longRunning = GlobalScopeMonitor.checkForLongRunningTasks(10000)
if (longRunning.isNotEmpty()) {
println("Warning: ${longRunning.size} long running tasks detected")
}
// 清理
job1.cancel()
GlobalScopeMonitor.cancelAll()
}
6. 替代 GlobalScope 的方案
6.1 应用级作用域
// 创建应用级别的协程作用域
class ApplicationScope private constructor() {
companion object {
private lateinit var instance: ApplicationScope
fun initialize(context: CoroutineContext = EmptyCoroutineContext) {
instance = ApplicationScope(context)
}
fun get(): ApplicationScope {
if (!::instance.isInitialized) {
initialize()
}
return instance
}
}
private val supervisorJob = SupervisorJob()
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
handleApplicationException(throwable)
}
val applicationScope: CoroutineScope
private constructor(context: CoroutineContext) : this() {
applicationScope = CoroutineScope(
Dispatchers.Default +
supervisorJob +
exceptionHandler +
context +
CoroutineName("ApplicationScope")
)
}
fun launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
return applicationScope.launch(context, start, block)
}
fun launchIO(block: suspend CoroutineScope.() -> Unit): Job {
return launch(Dispatchers.IO, block = block)
}
fun cancelAll() {
supervisorJob.cancelChildren()
}
fun shutdown() {
applicationScope.cancel()
}
private fun handleApplicationException(throwable: Throwable) {
// 应用级别的异常处理
System.err.println("ApplicationScope exception: $throwable")
// 可以根据异常类型采取不同行动
when (throwable) {
is IOException -> {
// 网络错误,可以重试或降级
}
is SecurityException -> {
// 权限错误,需要用户干预
}
else -> {
// 其他未预料错误
// 可以发送到错误报告服务
}
}
}
}
// 在应用启动时初始化
fun main() {
// 初始化应用作用域
ApplicationScope.initialize(
Dispatchers.Default +
CoroutineExceptionHandler { _, e ->
println("App error: $e")
}
)
// 使用应用作用域而不是 GlobalScope
val appScope = ApplicationScope.get()
appScope.launch {
println("Running in application scope")
}
appScope.launchIO {
// IO 操作
}
// 在应用关闭时清理
Runtime.getRuntime().addShutdownHook(Thread {
ApplicationScope.get().shutdown()
})
}
6.2 功能模块作用域
// 为每个功能模块创建独立的作用域
abstract class FeatureScope(
protected val featureName: String
) : CoroutineScope {
protected open val supervisorJob = SupervisorJob()
protected open val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
handleFeatureException(throwable)
}
override val coroutineContext: CoroutineContext
get() = Dispatchers.Default + supervisorJob + exceptionHandler + CoroutineName(featureName)
abstract fun handleFeatureException(throwable: Throwable)
fun cancelFeature() {
supervisorJob.cancel()
}
fun launchIO(block: suspend CoroutineScope.() -> Unit): Job {
return launch(Dispatchers.IO, block = block)
}
}
// 具体功能模块
class AnalyticsScope : FeatureScope("Analytics") {
override fun handleFeatureException(throwable: Throwable) {
// 分析系统的异常可以静默处理
System.err.println("Analytics error (non-critical): $throwable")
}
fun trackEvent(eventName: String, properties: Map<String, Any> = emptyMap()) {
launchIO {
// 异步发送分析事件
sendAnalyticsEvent(eventName, properties)
}
}
private suspend fun sendAnalyticsEvent(eventName: String, properties: Map<String, Any>) {
// 实现发送逻辑
}
}
class DatabaseScope : FeatureScope("Database") {
override val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
// 数据库错误需要特殊处理
if (throwable is SQLException) {
handleDatabaseError(throwable)
} else {
super.handleFeatureException(throwable)
}
}
override fun handleFeatureException(throwable: Throwable) {
System.err.println("Database error: $throwable")
// 可能需要回滚事务或重试
}
private fun handleDatabaseError(error: SQLException) {
// 特定的数据库错误处理
}
fun queryUsers(): Job {
return launchIO {
// 数据库查询
}
}
}
// 使用功能模块作用域
fun demonstrateFeatureScopes() {
val analytics = AnalyticsScope()
val database = DatabaseScope()
// 分别在不同的作用域中执行任务
analytics.trackEvent("app_started")
database.queryUsers()
// 可以独立取消
analytics.cancelFeature()
// 或者通过应用统一管理
}
7. 总结:GlobalScope 的指导原则
何时使用 GlobalScope:
- 真正的全局任务:应用生命周期内的后台服务
- 日志和监控:不依赖组件生命周期的记录操作
- 工具函数:独立、自包含的异步工具
- 快速原型:临时测试代码(但生产环境要替换)
何时避免 GlobalScope:
- UI 组件中:Activity、Fragment、Composable
- ViewModel 或 Presenter 中
- 有明确生命周期的对象中
- 需要结构化并发的场景
最佳实践:
- 优先使用生命周期感知的作用域:
lifecycleScope、viewModelScope - 创建应用级作用域:替代 GlobalScope
- 总是提供取消机制:返回 Job 或使用可取消的接口
- 添加监控和日志:跟踪 GlobalScope 的使用
- 代码审查:限制团队中 GlobalScope 的使用
- 使用静态分析工具:检测不当的 GlobalScope 使用
记住:GlobalScope 是最后的备选方案,不是首选方案。在大多数情况下,都有更好的替代方案可以提供更安全、更可维护的并发代码。