任务,线程和同步(三)之取消架构

.NET4.5引入了一种取消架构,允许开发者以标准化的方式取消长时间运行的任务。该架构基于协作行为而非强制性,支持取消的任务可以通过检查CancellationToken来决定是否终止。本文通过Parallel.For方法和任务两个实例展示了如何利用这一特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

取消架构

.NET4.5包含一个取消架构,允许以标准方式取消长时间运行的任务。每个阻塞调用都应支持这种机制。当然目前不是所有的阻塞都实现了这种新的技术,但越来越多的阻塞调用都支持它。已提供了这种机制的技术有任务,并发集合类,并行LINQ和几种同步机制。
取消架构基于协作行为,它不是强制的。长时间运行的任务会检查它是否被取消,并返回控制权。
支持取消的方法接受一个CancellationToken参数。这个类定义了IsCancellationRequestd属性,其中长时间运行的操作可以检查它是否应终止。

1.1Parallel.For()方法的取消

    var cts = new CancellationTokenSource();
            cts.Token.Register(() => Console.WriteLine("*** token canceled"));
            cts.CancelAfter(500);

            try
            {
                ParallelLoopResult result = Parallel.For(0, 100, new ParallelOptions()
                {

                    CancellationToken = cts.Token,
                }, x =>
                {
                    Console.WriteLine(" loop {0} started", x);
                    int sum = 0;
                    for (int i = 0; i < 100; i++)
                    {
                        Thread.Sleep(20);
                        sum += i;
                    }
                    Console.WriteLine("loop {0} finished", x);
                });
            }
            catch (OperationCanceledException ex)
            {

                Console.WriteLine(ex.Message);
            }

运行应用程序,会得到如下结果,第0,25,50,75,1迭代启动,在一个有4个内核CPU上系统运行。通过取消此操作,其他所有的迭代操作都在启动之前取消了。启动的迭代操作允许完成,因为取消操作总是以协作方式进行,以避免在取消迭代操作的中间泄露资源。

这里写图片描述

2.任务取消

同样的取消模式也适用于任务。首先,新建一个CancellationTokenSource、如果仅需要一个取消标记,就可以访问Task.Factory.CancellationToken以使用默认的取消标记。

   private static void TRaskCancell()
        {
            var cts = new CancellationTokenSource();
            cts.Token.Register(() => Console.WriteLine("*** token canceled"));
            cts.CancelAfter(500);

            Task t1 = Task.Run(() => {
                Console.WriteLine("in task");
                for (int i = 0; i < 20; i++)
                {
                    Thread.Sleep(100);
                    CancellationToken token = cts.Token;
                    if (token.IsCancellationRequested)
                    {
                        Console.WriteLine("cancelling was requested"+"cancelling from within the task");
                        token.ThrowIfCancellationRequested();
                        break;
                    }
                    Console.WriteLine("in loop ");
                }
                Console.WriteLine("task finished without cancelation");

            },cts.Token);

            try
            {
                t1.Wait();
            }
            catch (AggregateException ex)
            {
                Console.WriteLine("exception:{0},{1}",ex.GetType().Name,ex.Message);
                foreach (var item in ex.InnerExceptions)
                {
                    Console.WriteLine("inner excetion:{0},{1}",ex.InnerException.GetType().Name,ex.InnerException.Message);
                }
            }

        }

运行应用程序,可以看到任务启动了,运行了几个循环,并获得了取消请求。之后取消任务,并抛出TaskCanceledException异常,它是从方法调用ThrowIfCancellationRequested()中启动。调用等待任务时,会捕获AggregateException异常,它还包含内部异常TaskCanceledException。

这里写图片描述

<think>嗯,用户想要在图书馆管理系统的场景下解决多线程数据同步的问题。我需要从系统设计并发控制的角度给出具体方案。图书馆管理系统确实存在典型的并发场景:当多个学生同时借阅同一本热门书籍,或者管理员在更新图书信息时进行借阅操作,都需要保证数据一致性。用户提到"多线程数据同步",说明已经意识到线程安全的重要性。参考引用的资料可以发现:引用[2]提到的采购管理系统数据库设计方法、权限控制事务处理经验可以迁移到图书馆系统;引用[3]讨论的数据库隔离级别索引优化也直接相关。特别是引用[2]强调的"系统性能优化""健壮稳定"正是用户需要的核心目标。深入分析用户的使用场景:首先是"借阅-归还"场景,存在库存同步问题;其次是"书籍信息更新"场景,可能产生读写冲突;还有"预定-取消"这类复杂的状态转换场景。每种场景需要不同的同步策略。技术上可以从四个层面构建解决方案:数据库层面使用事务乐观锁,应用层用Java并发工具(如ReentrantReadWriteLock处理读写分离),缓存层使用Redis原子操作,最后通过分布式锁扩展集群能力。特别要注意避免死锁——比如设定锁等待超时时间。性能方面要权衡:单纯的synchronized虽然安全但并发量低,而乐观锁+版本号的组合在冲突较少时效率更高。引用[3]提到的数据库隔离级别建议采用RepeatableRead以上级别。用户作为系统开发者可能还需要架构层面的建议:模块化设计(参考引用[2])、监控线程阻塞情况、压力测试工具选择等。这些虽然没明说但肯定是实际开发需要的。在扩展性上,随着图书馆分馆增多,当前的本地锁方案可能需要升级为Redis分布式锁(参考引用[1]的互联网应用经验)。不过初期单机部署时不需要过度设计。</think> 在图书馆管理系统中实现多线程数据同步的核心在于处理并发读写冲突,保障数据一致性。以下是针对不同场景的解决方案与最佳实践: --- ### **一、典型多线程场景** 1. **并发借阅/归还** 多个用户同时操作同一本热门书籍(如新上架图书),导致库存数据竞争。 2. **批量数据更新** 管理员批量修改图书信息时,用户同时发起借阅请求。 3. **预约冲突** 用户A预约时,用户B同时借走该书,导致状态不一致。 --- ### **二、分层解决方案** #### **1. 数据库层控制** ```sql -- 使用乐观锁(版本号控制) UPDATE books SET stock = stock - 1, version = version + 1 WHERE book_id = 1001 AND version = 当前版本 AND stock > 0; ``` - **优势**:避免长期锁表,适合高并发场景[^2] - **隔离级别**:推荐使用 **Repeatable Read**(MySQL默认级别),防止脏读不可重复读[^3] #### **2. 应用层同步** ```java // 使用显式锁控制关键操作 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public boolean borrowBook(int bookId) { lock.writeLock().lock(); // 写锁独占 try { Book book = dao.loadBook(bookId); if(book.getStock() > 0) { book.setStock(book.getStock() - 1); dao.updateBook(book); return true; } return false; } finally { lock.writeLock().unlock(); } } ``` - **适用场景**:高频操作的库存变更 - **注意事项**:避免在锁内调用外部服务,防止线程阻塞[^2] #### **3. 缓存层优化** ```java // Redis原子操作保证库存同步 String key = "book:1001:stock"; Long remain = redisTemplate.execute( RedisScript.<Long>of("if redis.call('get', KEYS[1]) > 0 then " + "return redis.call('decr', KEYS[1]) " + "else return -1 end"), Collections.singletonList(key) ); if(remain >= 0) { // 成功扣减,异步更新数据库 } ``` - **作用**:将99%的并发请求拦截在缓存层,减轻数据库压力 - **数据一致性**:通过消息队列异步持久化到数据库[^1] --- ### **、进阶设计模式** 1. **线程本地存储** 使用`ThreadLocal`存储用户借阅上下文,避免传递请求参数。 ```java private static final ThreadLocal<User> userContext = new ThreadLocal<>(); // 在拦截器中设置 userContext.set(currentUser); ``` 2. **发布-订阅模式** 图书状态变更时通过事件通知关联模块: ```java applicationContext.publishEvent( new BookStatusEvent(this, bookId, "BORROWED") ); ``` - 监听者可即时更新:预约队列、推荐系统、馆藏地图[^2] --- ### **四、容错与监控** 1. **分布式锁超时** 设置锁自动释放时间(如Redisson的`lock.lock(10, TimeUnit.SECONDS)`),防止死锁。 2. **线程池隔离** 核心业务与批量任务使用独立线程池: ```java // 借阅操作使用高优先级线程池 ExecutorService borrowExecutor = Executors.newFixedThreadPool(10); // 报表生成使用低优先级线程池 ExecutorService reportExecutor = Executors.newCachedThreadPool(); ``` --- ### **五、验证方案** 1. **压力测试脚本**(JMeter模拟) - 100并发用户抢借50本相同书籍 - 验证最终库存是否为0且无超借 2. **日志追踪** 在关键操作添加TraceID: ```java MDC.put("traceId", UUID.randomUUID().toString()); ``` 通过日志系统聚合分析线程阻塞点。 --- ### **典型问题定位示例** ```log 2023-08-20 10:05:23 [WARN] [traceId=8a7d2f] 写锁等待超时: BookService.borrowBook 2023-08-20 10:05:24 [INFO] [traceId=8a7d2f] 库存同步延迟: Redis→DB 耗时 120ms ``` 需优化点:**扩大写锁等待队列** 或 **引入分布式缓存预扣减** ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值