




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
17/20多线程编程的并发性控制第一部分多线程面临的挑战 2第二部分死锁与活锁的发生原因 4第三部分临界区的定义与目的 6第四部分悲观锁和乐观锁的实现机制 8第五部分读者-写者问题与解决方案 10第六部分哲学家进餐问题与解决方案 13第七部分生产者-消费者问题与解决方案 15第八部分管程的结构与使用方法 17
第一部分多线程面临的挑战关键词关键要点资源竞争
1.两个或多个线程同时访问共享资源时,可能会发生资源竞争。这可能导致数据不一致和死锁。
2.资源竞争的一个常见例子是竞争共享内存中的数据。当两个线程同时试图修改相同的内存位置时,可能导致数据损坏。
3.另一个常见的资源竞争例子是竞争硬件资源,如处理器时间和内存。当两个线程同时试图执行代码时,可能会导致处理器时间和内存的使用效率低下。
死锁
1.死锁是指两个或多个线程等待彼此释放资源的情况。这可能导致系统崩溃。
2.死锁的一个常见例子是两个线程同时持有不同的锁,并等待彼此释放锁。这可能会导致两个线程都无法继续执行。
3.另一个常见的死锁例子是两个线程同时试图访问同一个资源,但其中一个线程已经持有该资源的锁。这可能会导致两个线程都无法访问该资源。
数据不一致
1.数据不一致是指两个或多个线程同时修改共享数据,导致数据不一致的情况。这可能导致程序出现错误。
2.数据不一致的一个常见例子是两个线程同时修改同一个变量。这可能会导致变量的值不正确。
3.另一个常见的数据不一致例子是两个线程同时修改同一个数据库表。这可能会导致数据库表中的数据不一致。多线程面临的挑战
多线程并发性控制是多线程程序设计中的重要课题,其复杂性主要源于以下挑战:
1.原子性违规:
原子性要求一个操作要么全部完成,要么根本不执行,没有中间状态。在多线程环境中,多个线程可能同时尝试访问或修改共享数据,从而导致原子性违规。例如,如果多个线程同时尝试向同一个账户转账,如果没有采取同步措施,可能会导致账户余额计算错误。
2.可见性问题:
可见性要求当一个线程修改了共享数据后,其他线程能够立即看到这个改变。在多线程环境中,由于线程调度和缓存等因素的影响,一个线程修改的数据可能不会立即对其他线程可见。这会导致其他线程使用旧的数据,从而导致程序错误。例如,如果一个线程修改了一个变量的值,而另一个线程立即读取该变量,如果由于缓存问题,读取到的值仍然是旧的值,那么就会产生错误的结果。
3.一致性问题:
一致性要求当多个线程同时修改共享数据時,最终结果是一致的。在多线程环境中,由于线程调度和同步机制的限制,多个线程可能以不同的顺序执行,这可能会导致最终结果不一致。例如,如果两个线程同时向同一个账户转账,如果先转账的线程在转账成功后立即读取账户余额,而另一个线程在转账成功前的某个时刻读取账户余额,则读取到的余额可能不一致。
4.死锁:
死锁是指两个或多个线程都在等待对方释放锁,导致所有线程都无法继续执行。在多线程环境中,当多个线程同时竞争同一个锁時,如果锁的获取和释放没有得到妥善处理,就可能导致死锁。例如,如果线程A持有锁1,并尝试获取锁2,同时线程B持有锁2,并尝试获取锁1,那么这两个线程就会陷入死锁,无法继续执行。
5.饥饿:
饥饿是指一个线程在很长时间内无法获得执行机会,导致该线程无法完成其任务。在多线程环境中,当多个线程同时竞争CPU资源时,如果调度算法不当,就可能导致某些线程长期无法获得执行的机会,从而导致饥饿。例如,如果一个线程优先级很低,而其他线程优先级都很高,那么低优先级的线程就可能会长期无法获得执行机会。
6.竞争条件:
竞争条件是指多个线程同时访问共享数据,并且至少有一个线程的访问不是原子操作,导致数据的不一致。在多线程环境中,当多个线程同时访问共享数据時,如果不存在同步机制来协调线程对共享资源的访问,就可能导致竞争条件。例如,如果两个线程同时尝试修改同一个变量的值,如果没有采取同步措施,那么最终的结果取决于哪个线程能够先修改该变量的值。第二部分死锁与活锁的发生原因关键词关键要点【死锁的发生原因】:
1.资源竞争:当多个线程同时请求相同的资源时,就会发生资源竞争。如果资源不可用,那么这些线程就会被阻塞,等待资源释放。
2.循环等待:当每个线程都持有另一个线程需要的资源时,就会形成循环等待。此时,没有任何线程可以继续执行,整个系统就会陷入死锁。
3.前占式资源分配:当线程可以在释放资源之前获得新的资源时,就会发生前占式资源分配。这可能会导致死锁,因为线程可能会持有过多的资源,而其他线程无法获得它们需要的资源。
【活锁的发生原因】:
死锁与活锁的发生原因
#死锁
资源竞争
死锁的本质是资源竞争,当多个线程同时请求同一组资源时,就会产生资源竞争。如果资源不可剥夺,即一个线程一旦获得资源,就不能被其他线程抢占,那么就有可能导致死锁。
循环等待
死锁的另一个必要条件是循环等待,即每个线程都等待另一个线程释放资源,而这个线程又等待另一个线程释放资源,如此循环下去,最终导致所有线程都无法继续执行。
解决方案
*预防死锁:
*死锁避免算法:在资源分配之前,检查是否有可能发生死锁,如果可能,则不进行资源分配。
*银行家算法:一种死锁避免算法,它通过跟踪每个线程对资源的需求和当前拥有的资源,来判断是否有可能发生死锁。
*检测死锁:
*死锁检测算法:一种在死锁发生后,检测死锁并采取措施解除死锁的算法。
*超时机制:如果一个线程在一定时间内无法获得所需的资源,则认为该线程发生了死锁,并采取措施解除死锁。
*解除死锁:
*资源剥夺:强行从一个线程手中剥夺资源,并将其分配给另一个线程。
*回滚:将一个或多个线程回滚到之前的一个状态,以释放资源。
*杀死线程:杀死一个或多个线程,以释放资源。
#活锁
资源依赖
活锁是指两个或多个线程相互等待对方释放资源,但实际上这些资源永远不会被释放。与死锁不同,活锁中的线程并没有持有对方所需的资源,而是依赖于对方释放的资源。
解决方案
*避免资源依赖:设计程序时,应尽量避免资源依赖,即一个线程不应该依赖于另一个线程释放的资源。
*使用锁:当多个线程需要访问共享资源时,可以使用锁来控制对共享资源的访问,以避免活锁。
*使用死锁检测和解除机制:如果活锁发生,可以使用死锁检测和解除机制来解除活锁。第三部分临界区的定义与目的关键词关键要点【临界区的定义】:
1.临界区是指一段代码,它在执行时不能被其他线程打断。
2.临界区通常用于保护共享资源,以防止多个线程同时访问同一资源而造成数据不一致或损坏。
3.临界区通常使用互斥锁或信号量等同步机制来实现,以确保只有一个线程能够在某个时刻进入临界区。
【临界区的目的】:
临界区的定义与目的
临界区(CriticalSection)是指一段代码,当一个线程进入临界区时,其他线程必须等待,直到该线程离开临界区才能继续执行。临界区的目的是确保共享资源在同一时间内只能被一个线程访问,从而防止数据损坏或其他问题。
临界区可以由各种机制来实现,如互斥锁、信号量和自旋锁等。其中,互斥锁是最常用的临界区实现机制。互斥锁是一种二进制信号量,它只能取两个值:加锁和解锁。当一个线程进入临界区时,它会先获取互斥锁,然后才能执行临界区内的代码。当该线程离开临界区时,它会释放互斥锁,从而允许其他线程进入临界区。
临界区的使用可以有效地防止共享资源的并发访问,从而确保数据的完整性和一致性。但在使用临界区时,也需要注意以下几点:
*临界区应该尽可能地小,只包含必须在互斥环境下执行的代码。
*临界区应该避免嵌套,即一个临界区内不能再调用其他临界区。否则,可能会导致死锁。
*临界区应该避免长时间占用,否则可能会导致其他线程长时间等待,降低系统的性能。
为了避免这些问题,可以使用一些优化技术,如细粒度锁、读写锁和无锁数据结构等。这些技术可以减少临界区的粒度,提高并发性并降低死锁的风险。
总之,临界区是多线程编程中一种重要的并发控制机制,它可以确保共享资源在同一时间内只能被一个线程访问,从而防止数据损坏或其他问题。在使用临界区时,需要注意临界区的粒度、嵌套和长时间占用等问题,并可以使用一些优化技术来提高临界区的性能和安全性。第四部分悲观锁和乐观锁的实现机制关键词关键要点【悲观锁】:
1.悲观锁是以“数据库行”为单位进行锁定,在查询数据库行时,会将其锁定,其他用户需要等待该行解锁后才能进行操作。
2.悲观锁的实现方式有两种:行锁和表锁。行锁仅锁定当前操作的行,而表锁则锁定整个表。
3.悲观锁可以保证数据的一致性,但由于锁的机制,可能会导致数据库性能下降。
【乐观锁】:
#悲观锁和乐观锁的实现机制
悲观锁
悲观锁又称为悲观并发控制,它是以“数据在任何时候都可能遭到破坏”这样一个悲观的态度看待并发控制。因此,悲观并发控制采取了排他性的加锁机制来防止数据遭到破坏。当一个事务开始处理数据时,它会为所涉及的数据加上锁,阻止其他事务同时访问被锁住的数据。这样,数据就不会受到破坏。
悲观锁的实现机制通常是通过数据库的锁机制来实现的。数据库中的锁可以分为表锁和行锁。表锁是对整个表进行加锁,而行锁是对表中的某一行或某几行进行加锁。当事务需要对某张表进行操作时,它会先对该表加上锁,然后才能对表中的数据进行访问。当事务结束时,它会释放锁。
悲观锁的优点是能够很好地防止数据遭到破坏。但是,悲观锁也存在一些缺点。首先,悲观锁会降低并发性。因为当一个事务对数据加上锁后,其他事务就无法访问被锁住的数据,这会降低数据库的并发能力。其次,悲观锁可能会导致死锁。因为当两个事务同时对两个数据加上锁时,就会产生死锁。
乐观锁
乐观锁又称为乐观并发控制,它是以“数据在任何时候都不会遭到破坏”这样一个乐观的态度看待并发控制。因此,乐观并发控制采取了非排他性的加锁机制来解决并发控制问题。当一个事务开始处理数据时,它不会对数据加上锁,而是记录下该数据在事务开始处理时的状态。当事务结束时,它会将数据修改后的状态与事务开始处理时的状态进行比较,如果数据在事务开始处理时与事务结束时是相同的,那么事务就可以提交数据。否则,事务就会回滚。
乐观锁的实现机制通常是通过数据库中的版本号来实现的。数据库中的每一行数据都会有一个版本号,当事务开始处理数据时,它会记录下该数据的版本号。当事务结束时,它会将数据修改后的版本号与事务开始处理时的版本号进行比较,如果数据的版本号在事务开始处理时与事务结束时是相同的,那么事务就可以提交数据。否则,事务就会回滚。
乐观锁的优点是能够提高并发性。因为乐观锁不会对数据加上锁,所以其他事务可以同时对数据进行访问。乐观锁的缺点是可能会导致数据遭到破坏。因为当两个事务同时对同一个数据进行修改时,两个事务都会认为数据在事务开始处理时与事务结束时是相同的,那么两个事务都可以提交数据,这会导致数据遭到破坏。第五部分读者-写者问题与解决方案关键词关键要点【读者-写者问题】:
1.问题描述:读者-写者问题是一个经典的多线程并发控制问题,描述了多个读者和一个或多个写者同时访问共享数据的场景,其中读者只能读取数据,而写者可以修改数据。
2.问题关键:如何协调读者和写者的访问,以保证数据的完整性(防止读者在写者写入数据时读取到不一致的数据)和写者的独占性(防止多个写者同时修改数据)。
3.解决方案:为了解决读者-写者问题,提出了多种并发控制策略,包括:
-读者优先策略:允许多个读者同时读取数据,但当有写者请求修改数据时,所有读者必须等待写者完成修改。
-写者优先策略:允许写者独占地修改数据,当有读者请求读取数据时,写者必须等待读者完成读取。
-优先级策略:根据读者和写者的优先级来决定谁可以访问数据。
-信号量策略:使用信号量来协调读者和写者的访问,确保数据的完整性和写者的独占性。
【读者-写者问题的解决方案】:
读者-写者问题与解决方案
#读者-写者问题
读者-写者问题是经典的并发控制问题之一,它描述了一个共享资源(例如一个数据库)由多个读者和一个写者同时访问的情况。读者可以同时访问该资源,而写者在访问该资源时需要独占该资源。
读者-写者问题的目标是设计一种并发控制机制,以确保读者和写者能够同时访问共享资源,而不会发生数据不一致的情况。
#读者优先解决方案
读者优先解决方案是一种常用的读者-写者问题解决方案。该解决方案允许读者优先访问共享资源,而写者只能在没有读者访问该资源时才能访问该资源。
读者优先解决方案的实现方式如下:
*将共享资源的访问权限分为两种:读锁和写锁。
*当一个读者想要访问共享资源时,它会获取一个读锁。
*当一个写者想要访问共享资源时,它会获取一个写锁。
*如果一个写者想要获取写锁,而此时共享资源已经被一个或多个读者锁定了,那么写者需要等待,直到所有的读者都释放了读锁。
*如果一个读者想要获取读锁,而此时共享资源已经被一个写者锁定了,那么读者可以继续获取读锁,但它必须等待,直到写者释放了写锁。
#写者优先解决方案
写者优先解决方案是一种常用的读者-写者问题解决方案。该解决方案允许写者优先访问共享资源,而读者只能在没有写者访问该资源时才能访问该资源。
写者优先解决方案的实现方式如下:
*将共享资源的访问权限分为两种:读锁和写锁。
*当一个读者想要访问共享资源时,它会获取一个读锁。
*当一个写者想要访问共享资源时,它会获取一个写锁。
*如果一个写者想要获取写锁,而此时共享资源已经被一个或多个读者锁定了,那么写者可以继续获取写锁,但它必须等待,直到所有的读者都释放了读锁。
*如果一个读者想要获取读锁,而此时共享资源已经被一个写者锁定了,那么读者需要等待,直到写者释放了写锁。
#两阶段锁定协议
两阶段锁定协议是一种常用的读者-写者问题解决方案。该协议将读者和写者的访问分为两个阶段:获取锁阶段和释放锁阶段。
两阶段锁定协议的实现方式如下:
*当一个读者想要访问共享资源时,它会先获取一个读锁,然后在释放读锁之前获取一个写锁。
*当一个写者想要访问共享资源时,它会先获取一个写锁,然后在释放写锁之前获取一个读锁。
*如果一个读锁和一个写锁同时请求访问共享资源,那么写锁优先。
*如果两个读锁同时请求访问共享资源,那么两个读锁都可以访问共享资源。
#总结
读者-写者问题是一个经典的并发控制问题,它描述了一个共享资源由多个读者和一个写者同时访问的情况。读者-写者问题有多种解决方案,包括读者优先解决方案、写者优先解决方案和两阶段锁定协议。第六部分哲学家进餐问题与解决方案关键词关键要点【哲学家进餐问题】:
1.问题描述:哲学家进餐问题描述了五个哲学家坐在一个圆桌旁,并共用五个叉子吃饭的情况。每个哲学家都有两个叉子,一个在自己左边,另一个在自己右边。哲学家只能用自己左边的叉子和右边的叉子吃饭,并且不能同时使用两个叉子。当一个哲学家想吃饭时,他必须先拿起自己左边的叉子,然后再拿起自己右边的叉子。如果一个哲学家拿起自己左边的叉子,发现自己右边的叉子已被其他哲学家拿起,那么他必须放下自己左边的叉子,并等待其他哲学家放下右边的叉子。
2.死锁问题:哲学家进餐问题中可能出现死锁问题。死锁是指两个或多个哲学家互相等待对方放下叉子,导致所有哲学家都无法继续吃饭的情况。
3.解决方法:哲学家进餐问题可以通过各种方法来解决。一种方法是使用信号量来控制对叉子的访问。另一种方法是使用优先级来控制对叉子的访问。
【可口可乐的原料比例问题】:
#《多线程编程的并发性控制》之哲学家进餐问题与解决方案
哲学家进餐问题及挑战
哲学家进餐问题是计算机科学中一个经典的并发编程问题,它通过五个哲学家围坐在一张圆桌旁试图进餐来形象地说明并发进程在竞争共享资源时可能面临的死锁或饥饿问题。
哲学家之间有如下规则:
1.每个哲学家都有两根筷子,并且只关心自己是否能够同时拿到两根筷子,从而开始进餐。
2.在同一时刻,最多只有两个哲学家能够同时进餐。
3.哲学家不能同时使用两根筷子。
解决方案:限制获取筷子的数量
一种简单的解决方案是限制哲学家获取筷子的数量,从而确保任何时刻最多只有两个哲学家能够同时进餐。
具体实现方式:
1.为每个筷子创建一个互斥锁。
2.为每个哲学家创建一个线程,线程将循环执行以下步骤:
*尝试获取左边的筷子的互斥锁。
*如果成功,则尝试获取右边的筷子的互斥锁。
*如果成功,则开始进餐。
*进餐结束后,释放左右筷子的互斥锁。
3.确保每个哲学家在获取筷子时按照相同的顺序进行,这样可以避免死锁。
这种方法简单易懂,但存在一些问题:
1.哲学家进餐的速度是一样的吗?如果一个哲学家进餐很快,那么另一个哲学家可能不得不等待很长时间才能拿到筷子。
2.哲学家在获取筷子时是否会发生争抢?如果发生争抢,那么可能会导致死锁。
解决方案:使用信号量
为了解决上述问题,可以使用信号量来控制哲学家获取筷子的顺序。
信号量是一种同步机制,它允许一个线程等待另一个线程完成某个操作。
具体实现方式:
1.为左右筷子各创建一个信号量,初始值为1。
2.为每个哲学家创建一个线程,线程将循环执行以下步骤:
*尝试获取左边的筷子的信号量。
*如果成功,则尝试获取右边的筷子的信号量。
*如果成功,则开始进餐。
*进餐结束后,释放左右筷子的信号量。
3.确保每个哲学家在获取筷子时按照相同的顺序进行,这样可以避免死锁。
这种方法可以确保哲学家进餐的速度是相同的,并且不会发生争抢。
总结
哲学家进餐问题是一个经典的并发编程问题,它通过形象的比喻说明了并发进程在竞争共享资源时可能面临的死锁或饥饿问题。通过使用信号量,我们可以解决这个问题,并确保所有哲学家能够公平地进餐。第七部分生产者-消费者问题与解决方案关键词关键要点【生产者-消费者问题】:
1.生产者-消费者问题描述:在并发编程中,多个生产者线程负责生成数据,多个消费者线程负责消费数据时,如果生产者生成数据的速度快于消费者消费数据的速度,就会导致数据堆积,从而浪费内存空间;如果消费者消费数据的速度快于生产者生成数据的速度,就会导致消费者等待,从而浪费CPU时间。解决这个问题需要一种机制来协调生产者和消费者的速度,以确保数据生产和消费之间的平衡。
2.经典的生产者-消费者问题解决方案:缓冲区(Buffer):一个共享的内存区域,用以在生产者和消费者之间交换数据。
信号量(Semaphore):一种同步机制,用于协调生产者和消费者的访问。
生产者线程:当缓冲区为空时,生产者线程将数据放入缓冲区,并对缓冲区进行加锁;当缓冲区已满时,生产者线程等待,直到缓冲区有空闲空间。
消费者线程:当缓冲区不为空时,消费者线程从缓冲区中取出数据,并对缓冲区进行解锁;当缓冲区为空时,消费者线程等待,直到缓冲区中有数据。
【同步机制】:
生产者-消费者问题与解决方案
生产者-消费者问题是一个经典的并行编程问题,它描述了两个或多个线程如何共享一个缓冲区。在生产者-消费者问题中,生产者线程将数据放入缓冲区,而消费者线程从缓冲区中取出数据。这个共享的缓冲区有有限的容量,因此生产者线程不能在缓冲区已满时继续生产数据,而消费者线程不能在缓冲区为空时继续消费数据。
生产者-消费者问题有几种不同的解决方案,最常见的是使用信号量来控制对缓冲区的访问。信号量是一种同步原语,它允许线程等待某个条件的满足。在生产者-消费者问题中,可以使用两个信号量来控制对缓冲区的访问:满信号量(fullsemaphore)和空信号量(emptysemaphore)。
满信号量用于控制生产者线程对缓冲区的访问。当缓冲区已满时,满信号量为0,这表示生产者线程不能继续生产数据。当缓冲区中有空闲空间时,满信号量为正数,这表示生产者线程可以继续生产数据。
空信号量用于控制消费者线程对缓冲区的访问。当缓冲区为空时,空信号量为0,这表示消费者线程不能继续消费数据。当缓冲区中有数据时,空信号量为正数,这表示消费者线程可以继续消费数据。
生产者线程在生产数据之前,首先检查满信号量是否为0。如果满信号量为0,则表示缓冲区已满,生产者线程必须等待。当缓冲区中有空闲空间时,满信号量为正数,生产者线程可以生产数据并递减满信号量。
消费者线程在消费数据之前,首先检查空信号量是否为0。如果空信号量为0,则表示缓冲区为空,消费者线程必须等待。当缓冲区中有数据时,空信号量为正数,消费者线程可以消费数据并递增空信号量。
使用信号量来控制对缓冲区的访问可以有效地防止生产者线程和消费者线程同时访问缓冲区,从而避免数据损坏。
除了使用信号量之外,还可以使用其他方法来解决生产者-消费者问题,例如使用管道、消息队列和共享内存。第八部分管程的结构与使用方法关键词关键要点【管程的定义】:
1.管程是一个共享的程序段,具有共享变量和原语操作。
2.原语操作是只能由一个进程在同一时间执行的,从而保证共享变量的正确性。
3.管程的结构:由一个管程定义和若干个管程调用组成。
【管程的并发性控制】:
#管程的结构与使用方法
在多线程编程中,管程是一种用于实现线程同步和通信的基本机制。管程由以下组成:
-变量:管程包含一组共享变量,这些变量只能由管程内的线程访问。
-操
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 山东工商学院《线性代数及概率统计》2023-2024学年第一学期期末试卷
- 内蒙古医科大学《生物学文献检索与论文写作》2023-2024学年第二学期期末试卷
- 湖北汽车工业学院科技学院《中国古典舞Ⅳ》2023-2024学年第一学期期末试卷
- 厦门医学院《工程识图与建筑构造》2023-2024学年第二学期期末试卷
- 天津天狮学院《手绘表现技法景观》2023-2024学年第二学期期末试卷
- 新乡医学院三全学院《研究方法与文献检索实训》2023-2024学年第一学期期末试卷
- 室内装修拆除施工合同
- 货物运输的安全合同
- 房地产转让合同协议
- 技术咨询服务合同书
- 马雷军-学校安全管理的基本知识
- 重力式码头工程完整施工组织设计(沉箱方案)
- (完整版)译林英语四年级下知识点及语法汇总
- (高清版)民用建筑修缮工程施工标准JGJ_T 112-2019
- 苏教版五年级数学下册第四单元易错题梳理和重难提升(含答案)
- 走进高端市场郑荣禄
- 节流式压差流量计
- 一只猫的生命哲学The Zen of Cat(中英文)
- 隧道地表预注浆技术交底(共7页)
- 通信的知识--家长进课堂(课堂PPT)
- 搅拌站调度室管理制度和奖罚办法
评论
0/150
提交评论