用于检测多线程程序中潜在竞争的方法和系统的制作方法

文档序号:6545004阅读:164来源:国知局
专利名称:用于检测多线程程序中潜在竞争的方法和系统的制作方法
技术领域
本发明一般涉及计算机系统,尤其涉及在多线程计算机程序中检测竞争条件。
背景技术
计算机软件开发者通常编写使用多线程执行的程序。现代的操作系统和编程语言支持线程,且许多大型商业应用程序是多线程的。对于在一操作系统进程中实现多个异步的计算,线程尤其有用。例如,事件驱动应用程序通常采用多线程。
然而,正是使得多线程成为有用的编程技术的特征也使得调试多线程程序成为非常困难的任务。多线程可以用非确定和时间相关的方式交互。通常这样的线程共享数据,需要它们的交互同步以确保程序的正确性,而与如何调度线程或如何交叉其指令流无关。
对编程者而言,在与竞争条件相关联的线程同步中检测错误尤其困难。在多线程程序中,当两个或更多并发线程访问共享存储单元却没有适当的同步限制访问顺序时(其中至少一个访问是写入),就产生数据竞争条件。在这种情况下,执行的效果取决于访问发生的特定顺序。竞争条件常会导致意想不到的和不可预期的程序动作,诸如程序崩溃或错误的结果。这种不确定性也正是使用常规的调试技术难以检测竞争条件的精确原因。
给定竞争条件的潜在有害影响和调试包含它们的程序的难度,用于检测竞争条件存在的自动工具对于多线程程序的开发者而言应该是很有价值的。然而,缺少有用的和高效的工具。对于动态竞争检测,其中在特定的程序执行中作出检测潜在竞争的尝试,已有两种广泛使用的方法Lamport的“在前发生”排序和锁集技术,这会在下面详细描述。前者通常具有令人非常不满的运行时间消耗,尤其是对于用诸如C#和Java等面向对象语言编写的程序,而后者则经常产生数量惊人的假阳性,尤其在使用异步代理的程序中。

发明内容
以下提出了本发明一些实施例的简要归纳以用于提供对本发明的基本理解。该归纳不是对本发明的广泛概述。它并非意在标识本发明的关键或重要元素或描绘本发明的范围。其唯一目的是以简化形式呈现本发明的一些实施例,作为以下更详细描述的序言。
依照本发明一实施例,提供动态竞争检测的系统。该系统包括用于维持访问共享存储单元的并发线程分段集的机制;用于相对运行线程,维持排列在当前线程分段前的线程分段集的机制;用于维持与共享存储单元相关联的第一锁集的机制;用于维持第二锁集的机制,该第二锁集与在其中获取和释放锁的线程相关联;用于报告检测到的竞争条件的机制。
根据另一实施例,提供了用于动态竞争检测的系统。该方法包括(a)维持与共享存储单元相关联的第一锁集;(b)维持第二锁集,该第二锁集与在其中获取和释放锁的线程相关联;(c)维持访问共享存储单元的并发线程分段集;以及(d)相对一线程,维持排列在当前线程分段前的线程分段集。
访问共享存储单元的并发线程分段集和排在线程前的线程分段集都可以被表示为排序对集,其中排序对集中对的一成员是线程标识符,而对的另一成员是用于标识该第一成员的线程分段的虚拟时钟值线程分段。
每个线程维持在线程创建时初始化为零的一虚拟时钟,只要线程衍生另一线程就将时钟加一。当线程衍生第二线程时,排在第二线程前的线程分段集被计算成以下的并集(i)排在第一线程前的线程分段集;以及(ii)包含第一线程线程分段的单元素集,在所述第一线程处衍生出第二线程。在一实施例中,将与第一线程相关联的虚拟时钟递增1,而将与衍生线程相关联的虚拟时钟初始化为0。当一线程对第二线程执行连接操作时,排在第一线程前的该线程分段集被计算成以下的并集(i)排在第一线程前的线程分段集;(ii)排在第二线程前的线程分段集的子集,其中对于该子集中每个线程分段,该线程分段的线程标识符不等于第一线程的线程标识符;以及(iii)包含第二线程的当前线程分段的单元素集。
如果线程访问共享存储单元,则通过移除不再并发地访问该位置的线程分段和添加该线程的当前线程分段来更新访问该单元的并发线程分段集。如果新的并发线程分段集包含不超过一元素,那么与共享存储位置相关联的锁集被更新为与该线程相关联的锁集,否则则被更新为与共享存储单元相关联的锁集和与该线程相关联的锁集的交集。如果当前线程分段集具有超过一个元素,并且与共享存储单元相关联的锁集是空,则报告现在竞争条件的警告。
根据另一个实施例,提供动态竞争检测的系统和方法。在一运行系统中,当在执行引擎中载入并编译通用中间语言形式的编码时,发出对竞争检测器的调用。由该运行期系统的存储分配机制将用于保存存储对象所需的装置信息的数据结构和该对象一起分配。
结合附图阅读以下详细描述,本发明的其它特征将变得显然,其中


图1是示出根据本发明与两个并发线程相关联的事件的”在前发生”排序的示图;图2A是示出根据本发明与线程相关联锁集的维持的流程图;图2B是示出根据本发明与共享存储单元相关联锁集的维持的流程图;图3是示出根据本发明在没有竞争条件的示例上锁集方法操作的示图;图4是示出根据本发明,在具有正确报告竞争条件的示例上锁集方法操作的示图;图5是示出根据本发明在锁集方法下,执行衍生和连接、导致假阳性的线程的操作的示图;图6是示出根据本发明实施例,执行衍生和连接线程的操作、示出对线程虚拟时钟的维持的示图;图7A是示出根据本发明实施例与衍生调用相关联步骤的流程图;图7B是示出根据本发明实施例,与连接调用相关联的步骤的流程图;图8是示出根据本发明实施例,与读取或写入存储单元相关联的步骤的流程图;图9是示出根据本发明实施例执行衍生和连接的线程操作的图示,其中对于存储单元的单线程访问,正确地不报告竞争条件。
具体实施例方式
在以下说明书中,将描述本发明的各个实施例。为了解释,陈述了特定配置和细节以提供对诸实施例的全面理解。然而,没有这些细节也可以实现本发明,这对本领域的技术人员是显而易见的。此外,略去或简化了众所周知的特征以避免模糊所述实施例。
在继续本发明的描述前,将较为详细地描述在上述背景技术部分中提到的在前发生(“在前发生”)和锁集方法,以阐明本发明的新颖性和实用性。在“在前发生”在前发生方法中,创建了与并发执行中所有线程相关联的所有事件的部分排序。该排序基于21Commun.ACM 558-565(1978)中Lamport的“Time,Clocks,andthe Ordering of Events in a Distributed System”所描述的关系,在此引入作为参考。在单线程中,事件按照它们发生的顺序排序。在线程之间,事件根据锁的属性或线程所获取和释放的其它同步对象的属性排序。如果一个线程访问锁,而对该锁的下一访问是由不同线程作出,如果该锁的语义防止两个事件可在时间上交换的时间表,则第一访问被定义为第二访问“在前发生”。如果两个线程访问共享存储单元且随便地打乱访问顺序,则认为竞争已经发生。
作为”在前发生”方法的简要说明,设想两个并行线程t1和t2,每个执行以下代码片断acquire(l);//Acquire lock lwrite x; //Write shared location xrelease(l);//Release lock l图1示出了与两个线程t1101和t2103相关联的事件的可能排序。由t1101执行的三个程序语句105、107、109以“在前发生”排序,因为它们在同一线程中顺序地执行。t2103对锁l的获取与t1对锁l的释放由“在前发生”排序,因为特定的锁不能在被它前一持有者释放之前被获取。最后,由t2103执行的三个语句111、113、115由”在前发生”排序,因为它们在该线程中顺序执行。
在多篇参考文章中描述了基于锁集的检测,诸如15 ACM Trans.Comp.Sys.391-411(1997)中Savage等人的“EraserA Dynamic Data Race Detector forMultithreaded Programs”,在此引入作为参考。在简单的锁集方法中,对于每个共享单元x,通过监控程序执行时对x的所有读取和写入来维持保护x的锁集Sx用于计算。对于每个线程t,通过监控线程t的锁的获取来维持t所持有的锁集St。
图2A和2B的流程图示出在锁集方法中如何分别维持集St和SX。参看图2A,在步骤201,当创建线程t时,St开始是空的。在步骤203,确定线程t是否取得了锁l。如果是,在步骤205通过St和包含1的单元素集的合集来更新St。相似地,在步骤207,确定线程是否释放了锁l。如果是,在步骤209,以St和包含1的单元素集的差集(即从由线程t所持有的锁集中移除1)来更新St。现在参看图2B,在步骤211,开始Sx为L,所有可能锁的集合。在步骤213,确定线程t是否对单元x执行读取或写入操作。如果是,在步骤215,将Sx更新为包含Sx和St的交集的集合。在步骤217,确定Sx是否为空。如果是,在步骤219报告关于潜在竞争条件的警告。
图3使用与图1中相同的示例性并发进程t1和t2来示出锁集方法操作的简示例。假设线程t1和t2用以下顺序执行其相应语句t1t21 acquire(l);4 acquire(l);2 write x; 5 write x;3 release(l);6 release(l);即,在线程t2执行三个语句之前线程t1执行其三个语句。我们也假设St1和St2是空的,并且在执行开始时Sx是L。
现在参看图3,该流程图示出了线程t1301和t2303执行的语句顺序。框305示出开始时St1是空的,在t1301获得锁l后,St1变成包含1的集合。在框307处,t1执行x的写入,随后Sx也变成包含1的集合。在框309处,t1301释放锁l,而St1又变成空集。现在线程t1301已结束其执行,线程t2303开始。框311显示开始时St2是空的,在t2303获得锁l后,St2变成包含1的集合。在框313处,t2303写单元x,随后Sx变成由Sx和St2的交集所形成的集合。由于两个集合现在都是包含1的单元素集,Sx仍然是包含1的集合。在框315处,t2303释放锁l,于是St2又成为空集。在线程t1301和t2302执行期间,Sx都不是空的,因此锁集方法不会报告任何竞争条件。实际上,缺乏竞争条件直观上遵从了在两个线程中单个锁l都被用于保护x的事实。
图4提供了锁集方法操作的第二个示例。这里线程t1401和线程t2403分别使用不同的锁l1和l2,而不象前面示例在两个线程中用相同的锁来保护x。框405示出了开始St1是空的,在t1401获得锁l1后,St1变成包含l1的集合。在框407,t1401执行对x的写入,随后Sx也变成包含l1的集合。在框409,t1301释放锁l1,St1又变成空集。现在线程t1401结束其执行,线程t2403开始。框411示出开始St2是空的,在t2403获得锁l2后,St2变成包含l2的集合。在框413,t2403写入位置x,随后Sx变成由Sx和St2的交集所形成的集合。Sx是包含l1的集合,而St2是包含l2的集合,因此它们的交集是空集。因为Sx是空集,在执行期间的这个点上报告竞争条件。在框415,t2403释放锁l2,St2又变成空的。
虽然实现锁集方法的工具通常并不具有与使用“在前发生”关系的方法相关联的较差性能,所共知的是这样的工具会产生许多假阳性,对无竞争程序报告竞争条件。最常见的假阳性类别包括当线程衍生并连接(等待)系统调用时所引起的那些假阳性。图5示出了一示例。我们假设在执行开始时只有一个线程t。垂直线501表示线程t的执行。从线501延伸出的对角线503表示t衍生新线程t1。垂直线505表示线程t1的执行。在衍生之后,现在有2个同时执行的线程t和t1。向线501延伸的对角线507表示线程t连接线程t1即,线程t等待线程t1完成执行。
衍生503和连接507对线程t501和t1505中的事件加以隐式排序。在框509,伴有获得和释放锁l线程t执行对x的写入。在框511,线程t1执行相同的语句。对x的两次访问没有引起竞争条件,因为它们是由同一锁l所保护的。在框513,线程t执行对x的写入。此时线程t无需使用锁就可写入x,并且无需使用同一锁用以在线程2执行中保护x。然而,如框513中所示,在写入x之前,Sx是包含1的集合。在写入x之后,Sx变为空。因为Sx是空的,即使因为只有单个进程在执行并且无需保护对x的访问而明显没有竞争,锁集程序也会报告竞争。
本发明扩展锁集方法以消除其在衍生和连接环境中报告假阳性的趋势。除了与锁集方法一样记录每个线程和每个单元的锁集之外,本发明还包含另外两个集合。一个集合是集合Tx,包括访问共享存储单元x的并行线程分段集。当相关锁集为空且Tx的基数大于1时,就报告竞争。第二个新的集合是集合Bt包括排在t的当前线程分段之前的线程分段集。在一实施例中,Tx和Bt都被表示为多元组集{<t1,c1>,…,<tn,cn>}。该排序取决于对每个线程t的虚拟时钟Ct的使用。多元组<t,c>表示在虚拟时钟时间t时线程t的线程分段。
图6是表示衍生和连接的图示,示出在本发明一实施例中为每个线程维持虚拟时钟的方式。垂直线607表示线程t的执行。我们假设t的虚拟时钟开始为0(在指定点609处)。对角线611表示衍生线程t1的t。在线程的执行线607的这个点上(指定的613),t的虚拟时钟递增1。垂直线615表示所衍生线程t1的执行。线程t1的虚拟时钟被初始化为0(在指定点617处)。对角线619表示t对t1的连接。
图7A的流程图示出了线程t对新的并行线程t1的衍生相关联的步骤。在步骤703,确定线程t是否衍生线程t1。如果是,执行后续的操作。在步骤703,集合Bt1(排在线程t1之前的线程分段集)计算为线程t的相应集合和包括线程分段<t,Ct>的单元素集的并集。在步骤705,线程t的虚拟时钟递增1。在步骤707,新衍生线程t1的虚拟时钟被初始化为0。图7B的流程图示出了与线程t的连接相关联的步骤。在步骤711,确定线程t是否作出连接调用,等待线程t1完成执行。如果是,在步骤713,Bt的新值计算为以下三个集合的并集(i)Bt,(ii)集合Bt1中不属于当前线程t的线程分段集,以及(iii)包含t1的当前线程分段的单元素集。
图2A所述和上述的有关当获取和释放锁时对集合St的维持的步骤也用于本发明中,因此无需在此作进一步的描述。
图8的流程图示出了当线程t执行对单元x的读取和写入时所采取的步骤。在步骤801,计算集合Tx的新值,表示读取和写入之后并行访问单元x的线程分段。形成新Tx的并集的第二部分是包含t的当前线程分段<t,Ct>的集合。由于线程t在读取或写入x,显然t是应该在集合Tx中的线程之一。并集的第一部分表示集合Tx旧值的子集,其中不再并行访问该单元的任何线程都被滤除。在步骤803,确定新Tx的基数是否小于或等于1。如果是,当前最多有一个线程在访问x。然后,Sx的新值变成锁集St的当前值(步骤805)。否则,在步骤807,有多个并行线程访问x,Sx的新值变成包含Sx的旧值和St的交集的集合。在步骤809,确定(a)Sx的新值是否为空且(b)Tx新值的基数是否大于1。如果是,在步骤811报告潜在竞争条件。
现在参看图9,示出类似于图5的衍生和连接的图示,但其中竞争检测遵从本发明的方法。我们假设在执行开始时(框901),集合Tx和Bt为空,且Sx是包含锁l的单元素集。假设框905在框907之前执行。
在执行框905后,Sx是包含1的集合,Tx是包含线程分段<t,1>的集合。在执行框907之后,Sx维持原状,但Tx现在包含两个线程分段<t,1>和<t1,0>。在框909的连接之后,Bt变成包含线程分段<t,1>的集合。在t写入x之前,在框911,Sx是包含1的集合,而Tx仍然是包含两个线程分段<t,1>和<t1,0>的集合。在写入之后,Sx变成空,而Tx变成包含线程分段<t,1>的集合(t是并行访问x的唯一线程)。因为Tx的基数为1,根据本发明会正确地不报告竞争条件。
在现有技术中,在实现如在本说明书中所述本发明诸实施例所包括的该类竞争检测器中有两种广泛使用的方法。第一种方法是在源码或字节代码层中插入对存储访问的调用。第二种方法是在本机代码中插入对载入和存储指令的调用。当动态链入大量共享库时,先前的方法使得竞争检测工具运行缓慢,且更重要的是,这通常会造成较高的运行时间成本。在本发明某些实施例中,动态竞争检测器在运行时系统中实现。
在一实施例中,在微软公司的.NET构架的公共语言运行时中实现竞争检测器。修改了将字节代码编译成源码的JIT(实时)编译器,使得当动态载入和编译代码时,插入对竞争检测器的调用。修改了用于分配共享存储对象的机制,使得分配向对象加上竞争检测器所需的装置信息;然后由运行时间的垃圾收集程序来自动管理信息。
这种实现方式相对先前的技术有许多优点。第一,所有执行的代码是动态测量的。第二,修改JIT而非字节代码避免了字节代码校验阶段的某些问题。第三,使JIT插入对竞争检测器的调用允许使用由编译器收集的信息来最优化该装置。例如,作为示例如果编译器确定一字段被声明为对特定线程为只读或本地并对其它线程不可见,编译器会确定该测量并不必需。第四,竞争检测器很好地与垃圾收集程序交互,只测量受控堆栈中的共享对象,并防止由竞争检测装置引起的潜在存储渗漏问题。最后,该实现提供了性能优势它消除了标准方法中所存在的对竞争检测器实现的间接程度。
其它变化在本发明的精神之内。因此,尽管本发明允许各种修改和可选构建,仅有其中某说明性实施例在附图中示出并如上进行了详细描述。然而,应该理解,在此并非旨在将本发明限制为特定形式或所揭示的形式,而相反其旨在包含所有落于本发明精神和范围之内的修改、可选构建和等效方案,如所附权利要求书中所定义。
在此描述的所有方法可以任何合适的顺序执行,除非这里另有指示或者明显地与上下文相抵触。在此提供的任意和所有示例或示例性语言(例如,“诸如”)的使用,仅仅旨在更好地说明本发明的诸实施例,而非对本发明的范围加以限制,除非另有声明。本说明书中的语言不应被理解为将任一未经声明的元素指示为实现本发明所必须的。
这里描述了本发明的优选实施例,包含发明者所知的实现本发明的最好模式。当阅读了前面的描述之后,那些优选实施例的变体对于本领域技术人员将变得显而易见。发明者期望技术人员适当地采用这些变体,且发明者期望本发明以在此特别描述的不同的方法实践。相应地,本发明包括可应用法令允许的所附权利要求书中所引用的本发明所有变体和等效方案。此外,所有可能变体中上述元素的任意组合包含在本发明中,除非在此另有指示或者明显地与上下文相抵触。
权利要求
1.一种在程序中用于动态检测潜在竞争条件的系统,所述程序具有多个线程和一个或多个共享存储单元,其特征在于,所述系统包括对于每个共享存储单元,(i)用于维持访问所述单元的并行线程分段集的机制,以及(ii)用于维持与所述单元相关联的第一锁集的机制;对于每个线程,(i)用于维持排列在所述线程当前线程分段之前的线程分段集的机制;以及(ii)用于维持由所述线程获取和释放的第二锁集的机制;以及用于在检测到潜在竞争条件时,报告警告的机制。
2.如权利要求1所述的系统,其特征在于,用于维持所述并行线程分段集的机制包括用于维持排序对集的机制,其中排序对集中对的一成员是线程标识符,而所述对的另一成员是与由所述线程标识符标识的线程相关联的虚拟时钟值。
3.如权利要求1所述的系统,其特征在于,用于维持排列在所述当前线程分段之前的线程分段集的机制包括用于维持排序对集的机制,其中排序对集中对的一成员是线程标识符,而对的另一成员是与由所述线程标识符所标识的线程相关联的虚拟时钟值。
4.一种用于在程序中动态检测潜在竞争条件的计算机实现方法,所述程序具有多个线程和一个或多个共享存储单元,其特征在于,所述方法包含对于每个共享存储单元,维持与所述单元相关联的第一锁集,以及维持访问所述单元的并行线程分段集;对于每个线程,维持所述线程获取和释放的第二锁集;以及维持排在线程的当前线程分段之前的线程分段集。
5.如权利要求4所述的方法,其特征在于,还包括对于每个线程,维持与所述线程相关联的虚拟时钟。
6.如权利要求5所述的方法,其特征在于,维持所述虚拟时钟包括当创建所述线程时,将所述虚拟时钟初始化为一初始值。
7.如权利要求6所述的方法,其特征在于,维持所述虚拟时钟包含当创建所述线程时,将所述虚拟时钟初始化为零。
8.如权利要求5所述的方法,其特征在于,维持排列在所述线程的当前线程分段之前的线程分段集包括维持排序对集,其中对的一成员是线程标识符,而对的另一成员是虚拟时钟值。
9.如权利要求8所述的方法,其特征在于,还包括如果第一线程衍生出第二线程将排在所述第二线程当前线程分段之前的线程分段集计算为以下集合的并集(a)排在第一线程当前线程分段之前的线程分段集;(b)包含所述第一线程当前线程分段的单元素集;递增与第一线程相关联的所述虚拟时钟;以及初始化与第二线程相关联的所述虚拟时钟。
10.如权利要求9所述的方法,其特征在于,递增与第一线程相关联的所述虚拟时钟包含将与第一线程相关联的虚拟时钟递增1。
11.如权利要求9所述的方法,其特征在于,初始化与第二线程相关联的所述虚拟时钟包含将与第二线程相关联的虚拟时钟初始化为零。
12.如权利要求8所述的方法,其特征在于,还包括如果第一线程连接所衍生的线程,将排列在第一线程当前线程分段之前的线程分段集计算为以下集合的合集(a)排列在第一线程当前线程分段之前的线程分段集,(b)包含排列在所衍生线程的当前线程分段之前但不属于第一线程的线程分段的集合,以及(c)包含所衍生线程的当前线程分段的单元素集。
13.如权利要求12所述的方法,其特征在于,排列在所衍生线程当前线程分段之前但不属于第一线程的线程分段集合包括包含排列在所衍生线程当前线程分段之前的线程分段集中排序对的集合,从而所述排序对中线程标识符不表示所述第一线程。
14.如权利要求8所述的方法,其特征在于,还包括,如果线程访问共享存储单元通过形成包括以下集合合集的集合,来更新访问所述单元的并行线程分段集(a)包含所述线程的当前线程分段的集合,以及(b)包含继续访问所述单元的并行线程分段集中线程分段的集合;以及如果所更新的并行线程分段集包含最多一个元素,则将与所述单元相关联的锁集更新为与所述线程相关联的锁集,否则(i)将与所述单元相关联的锁集更新为包含以下集合的交集的集合(a)与所述位置相关联的锁集以及(b)与所述线程相关联的锁集,以及(ii)如果与所述单元相关联的锁集为空,则报告潜在竞争条件的警告。
15.如权利要求14所述的方法,其特征在于,继续访问所述单元的并行线程分段集中线程分段的集合是通过计算并行线程分段集的子集所形成的,其中所述子集包含满足以下判定的每个线程分段a对于排列在a之前的线程分段集中每个线程分段b,至少以下一个为真(i)a的线程标识符不等于b的线程标识符以及(ii)a的虚拟时钟值大于b的虚拟时钟值。
16.一种动态竞争检测系统,其特征在于,所述系统包括一运行时系统的编译器,其在经编译代码中将插入对竞争检测器的调用;以及一运行时系统的存储分配器,其将所述竞争检测器所需的检测信息加到共享存储对象上。
17.如权利要求16所述的系统,其特征在于,所述编译器是另一编译器的变体。
18.如权利要求16所述的系统,其特征在于,所述存储分配器是另一存储分配器的变体。
19.一种用于动态竞争检测的计算机实现方法,其特征在于,所述方法包括经由运行时系统的编译器,在经编译的代码中插入对竞争检测器的调用;以及经由运行时系统的存储分配器,将所述竞争检测器所需的检测信息加到共享存储对象上。
20.如权利要求19所述的方法,其特征在于,插入对所述竞争检测器的调用经由修改所述运行时系统的编译器。
21.如权利要求20所述的方法,其特征在于,加上所述竞争检测器所需的检测信息经由改变所述运行时系统的存储分配器。
全文摘要
提供了克服先前锁集方法缺点的动态竞争检测系统和方法,所述先前锁集方法会产生许多假阳性,尤其是在线程衍生/连接和异步调用环境中。对于每个共享存储单元,维持有保护该单元的锁集和访问该单元的并行线程分段集。为了维持这些集合,每个线程维持当前持有的锁集和排列在其当前线程分段之前的线程分段集。每个线程也维持有在该线程衍生第二线程时递增的虚拟时钟。线程分段是包含线程标识符和虚拟时钟值的对。当特定共享存储单元的锁集为空且该存储单元的并行线程集的基数大于1时报告数据竞争。
文档编号G06F9/52GK1677353SQ20051006373
公开日2005年10月5日 申请日期2005年3月24日 优先权日2004年3月24日
发明者Y·于 申请人:微软公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1