C-线程本地存储-深化了解线程间值差别 (c++线程 window)
一:背景
1.讲故事
有好友在微信外面问我,为什么用ThreadStatic标志的字段,只要第一个线程拿到了初始值,其余线程都是自动值,让我能不能帮他解答一下,尼玛,我也不是神仙什么都懂,既然问了,那我试着帮他解答一下,也给前面相似不懂的好友解个惑吧。
二:为什么值不一样
1.疑问复现
为了繁难讲述,定义一个ThreadStatic的变量,而后用多个线程去访问,参考代码如下:
internalclassProgram{[ThreadStatic]publicstaticintnum=10;staticvoidMn(string[]args){Test();Console.ReadLine();}///<summary>///1.个性模式///</summary>staticvoidTest(){vart1=newThread(()=>{Debugger.Break();varj=num;Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId},num={j}");});t1.Start();t1.Join();vart2=newThread(()=>{Debugger.Break();varj=num;Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId},num={j}");});t2.Start();}}
图片
从代码中可以看到,确实如好友所说,一个是num=10,一个是num=0,那为什么会产生这样的状况呢?
2.从汇编上寻觅答案
作为C#程序员,真的须要把握一点汇编,往往就能找到疑问的打破口,先看一下thread1中的varj=num;所对应的汇编代码,参考如下:
D:codeMylicationConsoleApp7Program.cs@27:08893737b9a0dd6808movecx,868DDA0h0889373cba04000000movedx,408893741e84a234e71callcoreclr!JIT_GetSharedNonGCThreadStaticBase(79d75a90)088937468b4814movecx,dptr[eax+14h]08893749894df8movdwordptr[ebp-8],ecx
从汇编上可以看到,这个num=10是来自于eax+14h的地址上,而eax是JIT_GetSharedNonGCThreadStaticBase函数的前往值,弦外之音外围逻辑是在此方法里,可以到coreclr中找一下这段代码,简化后如下:
HCIMPL2(void*,JIT_GetSharedNonGCThreadStaticBase,DomainLocalModule*pDomainLocalModule,DWORDdwClassDomainID){FCALL_CONTRACT;//GettheModuleIndexModuleIndexindex=pDomainLocalModule->GetModuleIndex();//GettherelevantThreadLocalModuleThreadLocalModule*pThreadLocalModule=ThreadStatics::GetTLMIfExists(index);//IftheTLMhasbeenallocatedandtheclasshasbeenmarkedasinitialized,//getthepointertothenon-GCstaticsbaseandreturnif(pThreadLocalModule!=NULL&&pThreadLocalModule->IsPrecomputedClassInitialized(dwClassDomainID))return(void*)pThreadLocalModule->GetPrecomputedNonGCStaticsBasePointer();//IftheTLMwasnotallocatedoriftheclasswasnotmarkedasinitialized//thenwehavetogothroughtheslowpath//ObtaintheMethodTableMethodTable*pMT=pDomainLocalModule->GetMethodTableFromClassDomainID(dwClassDomainID);returnHCCALL1(JIT_GetNonGCThreadStaticBase_Helper,pMT);}
这段代码十分无心思,曾经把ThreadStatic玩法的骨架图给绘制进去了,大略意思是每个线程都有一个ThreadLocalBlock结构体,这个结构体下有一个ThreadLocalModule的字典,key为ModuleIndex,value为ThreadLocalModule,画个简图如下:
图片
从图中可以看到num是放在ThreadLocalModule中的,详细的说就是此结构的m_pDataBlob数组中,可以用windbg验证下。
0:008>reax=03077810ebx=08baf978ecx=79d75c10edx=03110568esi=053faa18edi=053fa9b8eip=08893746esp=08baf8d8ebp=08baf908iopl=0nvupeiplzrnapenccs=0023ss=002bds=002bes=002bfs=0053gs=002befl=00000246ConsoleApp7!ConsoleApp7.Program.<>c.<Test>b__2_0+0x46:088937468b4814movecx,dwordptr[eax+14h]ds:002b:03077824=0000000a0:008>dtcoreclr!ThreadLocalModule03077810+0x000m_pDynamicClassTable:(null)+0x004m_aDynamicEntries:0+0x008m_pGCStatics:(null)+0x00cm_pDataBlob:[0]""0:008>dp03077810+0x14L1030778240000000a
有了这些前置常识后,接上去就繁难了,假设以后的ThreadLocalModule不存在就会调用JIT_GetNonGCThreadStaticBase_Helper函数在m_pTLMTable字段中增加一项,接上去观察下这个函数代码,简化如下:
HCIMPL1(void*,JIT_GetNonGCThreadStaticBase_Helper,MethodTable*pMT){//GettheTLMThreadLocalModule*pThreadLocalModule=ThreadStatics::GetTLM(pMT);//CheckiftheclassconstructorneedstoberunpThreadLocalModule->CheckRunClassInitThrowing(pMT);//Lookupthenon-GCstaticsbasepointerbase=(void*)pMT->GetNonGCThreadStaticsBasePointer();returnbase;}PTR_ThreadLocalModuleThreadStatics::GetTLM(ModuleIndexindex,Module*pModule)//static{//GettheTLMifitalreadyexistsPTR_ThreadLocalModulepThreadLocalModule=ThreadStatics::GetTLMIfExists(index);//IftheTLMdoesnotexist,createitnowif(pThreadLocalModule==NULL){//AllocateandinitializetheTLM,andaddittotheTLB'stablepThreadLocalModule=AllocateAndInitTLM(index,pThreadLocalBlock,pModule);}returnpThreadLocalModule;}
上方这段代码的步骤很分明。
这个pMT十分关键,训练营里的好友都知道MethodTable是C#的class承载,弦外之音就是判别下这个class有没有被初始化,假设没有初始化那就调静态结构函数,接上去的疑问是class究竟是哪一个类呢?
联合刚才汇编中的movedx,4以及源码发现是取IL元数据中的Program,参考代码及截图如下:
FORCEINLINEMethodTable*GetMethodTableFromClassDomainID(DWORDdwClassDomainID){DWORDrid=(DWORD)(dwClassDomainID)+1;TypeHandleth=GetDomainFile()->GetModule()->LookupTypeDef(TokenFromRid(rid,mdtTypeDef));MethodTable*pMT=th.AsMethodTable();returnpMT;}
图片
也可以用windbg在JIT_GetNonGCThreadStaticBase_Helper方法的return处下一个断点,参考如下:
0:008>recxecx=0564ef280:008>!dumpmt0564ef28EEClass:056d14d0Module:0564db08Name:ConsoleApp7.ProgrammdToken:02000005File:D:codeMyApplicationConsoleApp7binx86Debug6.0ConsoleApp7.dllAssemblyLoadContext:DefaultALC-Themanagedinstanceofthiscontextdoesn'texistyet.BaseSize:0xcComponentSize:0x0DynamicStatics:falseContainsPointers:falseSlotsinVTable:8NumberofIFacesinIFaceMap:0
到这里就水落石出了,thread1在口头时,用CheckRunClassInitThrowing方法发现Program没有被静态结构过,所以就口头了,即num=10,当thread2口头时,发现曾经被结构过了,所以就不再口头静态结构函数,所以就成了自动值num=0。
3.如何复验你的论断
刚才我说thread1做了一个能否口头静态结构的判别,其实这里我可以做个手脚,在Main之前先把Program静态函数给口头掉,按理说thread1和thread2此时都会是自动值num=0,对不对,哈哈,试一试呗,简化代码如下:
internalclassProgram{[ThreadStatic]publicstaticintnum=10;///<summary>///先于main口头///</summary>staticProgram(){}staticvoidMain(string[]args){Test();Console.ReadLine();}}
图片
哈哈,此时都是0了,也就再次验证了我的论断。
三:总结
在C#开发中经常会有一些纳闷,假设不了解汇编,C++,置信你会堕入到很多的魔法经常使用中而苦于不能独自解惑的遗憾。
linuxc++多线程clinux多线程
c语言多线程详解?
概念
线程:线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。
多线程:多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
C语言的开始设计,并未设计多线程的机制,由于随着软硬件的发展及需求的发展。后来C语言才开发了线程库以支持多线程的操作、应用。
主要基于Linux介绍C多线程。在编译C的多线程时候,一方面必须指定LinuxC语言线程库多线程库pthread,才可以正确编译(例如:gcctest.c-otest-lpthread);另一方面要包含有关线程头文件#includepthread.h>。
linux怎么查看一个进程的所有线程?
使用ps命令
常用参数:
-a显示所有进程(等价于-e)(utility)
-a显示一个终端的所有进程,除了会话引线
-n忽略选择。
-d显示所有进程,但省略所有的会话引线(utility)
-x显示没有控制终端的进程,同时显示各个命令的具体路径。dx不可合用。(utility)
-ppid进程使用cpu的时间
-uuidorusername选择有效的用户id或者是用户名
-ggidorgroupname显示组的所有进程。
uusername显示该用户下的所有进程,且显示各个命令的详细路径。如:psuzhang;(utility)
-f全部列出,通常和其他选项联用。如:ps-faorps-fxandsoon.
-l长格式(有f,wchan,c等字段)
-j作业格式
-o用户自定义格式。
v以虚拟存储器格式显示
s以信号格式显示
-m显示所有的线程
-h显示进程的层次(和其它的命令合用,如:ps-ha)(utility)
e命令之后显示环境(如:ps-de;ps-ae)(utility)
h不显示第一行
ps命令常用用法:
1)psa显示现行终端机下的所有程序,包括其他用户的程序。
2)ps-a显示所有进程。
3)psc列出程序时,显示每个程序真正的指令名称,而不包含路径,参数或常驻服务的标示。
4)ps-e此参数的效果和指定a参数相同。
5)pse列出程序时,显示每个程序所使用的环境变量。
6)psf用ascii字符显示树状结构,表达程序间的相互关系。
7)ps-h显示树状结构,表示程序间的相互关系。
8)ps-n显示所有的程序,除了执行ps指令终端机下的程序之外。
9)pss采用程序信号的格式显示程序状况。
10)pss列出程序时,包括已中断的子程序资料。
11)ps-t指定终端机编号,并列出属于该终端机的程序的状况。
12)psu以用户为主的格式来显示程序状况。
13)psx显示所有程序,不以终端机来区分。
最常用的方法是ps-aux或ps-ef,然后再利用一个管道符号导向到grep去查找特定的进程,然后再对特定的进程进行操作。
Linux多线程通信?
PIPE和FIFO用来实现进程间相互发送非常短小的、频率很高的消息;
这两种方式通常适用于两个进程间的通信。
共享内存用来实现进程间共享的、非常庞大的、读写操作频率很高的数据(配合信号量使用);这种方式通常适用于多进程间通信。
其他考虑用socket。这里的“其他情况”,其实是今天主要会碰到的情况:分布式开发。
在多进程、多线程、多模块所构成的今天最常见的分布式系统开发中,socket是第一选择
。消息队列,现在建议不要使用了----因为找不到使用它们的理由。在实际中,我个人感觉,PIPE和FIFO可以偶尔使用下,共享内存都用的不多了。在效率上说,socket有包装数据和解包数据的过程,所以理论上来说socket是没有PIPE/FIFO快,不过现在计算机上真心不计较这么一点点速度损失的。你费劲纠结半天,不如我把socket设计好了,多插一块CPU来得更划算。另外,进程间通信的数据一般来说我们都会存入数据库的,这样万一某个进程突然死掉或者整个服务器死了,也不至于丢失重要数据、便于回滚到之前的状态。从这个角度考虑,适用共享内存的情况也更少了,所以socket使用得更多。再多说一点关于共享内存的:共享内存的效率确实高,但它的重点在“共享”二字上。如果的确有好些进程共享一大块数据(如果把每个进程都看做是类的对象的话,那么共享数据就是这个类的static数据成员),那么共享内存就是一个不二的选择了。但是在面向对象的今天,我们更多的时候是多线程+锁+线程间共享数据。因此共享进程在今天使用的也越来越少了。不过,在面对一些极度追求效率的需求时,共享内存就会成为唯一的选择,比如高频交易系统。除此以外,一般是不需要特意使用共享内存的。另外,PIPE和共享内存是不能跨LAN的
(FIFO可以但FIFO只能用于两个进程通信) 如果你的分布式系统随着需求的增加而越来越大所以你想把不同的模块放在不同机器上而你之前开发的时候用了PIPE或者共享内存,那么你将不得不对代码进行大幅修改......同时,即使FIFO可以跨越LAN,其代码的可读性、易操作性和可移植性、适应性也远没有socket大。这也就是为什么一开始说socket是第一选择的原因。最后还有个信号简单说一下。请注意,是信号,不是信号量。
信号量是用于同步线程间的对象的使用的(建议题主看我的答案,自认为比较通俗易懂:semaphore和mutex的区别?-Linux-知乎
)。信号也是进程间通信的一种方式。比如在Linux系统下,一个进程正在执行时,你用键盘按Ctrl+c,就是给这个进程发送了一个信号。进程在捕捉到这个信号后会做相应的动作。虽然信号是可以自定义的,但这并不能改变信号的局限性:不能跨LAN、信息量极其有限
。在现代的分布式系统中,通常都是消息驱动:
即进程受到某个消息后,通过对消息的内容的分析然后做相应的动作。如果你把你的分布式系统设置成信号驱动的,这就表示你收到一个信号就要做一个动作而一个信号的本质其实就是一个数字而已。这样系统稍微大一点的话,系统将变得异常难以维护;甚至在很多时候,信号驱动是无法满足我们的需求的。因此现在我们一般也不用信号了。因此,请记住:除非你有非常有说服力的理由,否则请用socket。
顺便给你推荐个基于socket的轻量级的消息库:ZeroMQ。linux多线程详解?
1.进程是操作系统分配资源的基本单位。而线程通俗来讲就是一个进程中一个执行流。
2.这里以串行与并行下载文件举例,如果我们使用串行的方式去下载多个文件,那么得到的结果是,将这些文件逐个按个的下载,即上一个下载完成之后才会下载接下来的文件。
3.如果使用并行的方式下载,那么这些文件就会一次同时下载多个文件,而不是等待上一个下载完后才继续下载接下来的,大大的提高了下载效率。
cpu多线程和jvm多线程?
一cpu个数、核数、线程数的关系
cpu个数:是指物理上,也及硬件上的核心数;
核数:是逻辑上的,简单理解为逻辑上模拟出的核心数;一个CPU核心数模拟出2线程的CPU
线程数:是同一时刻设备能并行执行的程序个数,线程数=cpu个数*核数,及程数=cpu个数(2)*核数(2)=4
Windows:wmic然后物理CPU数“cpugetNumberOfCores”,CPU核心数“cpugetNumberOfLogicalProcessors”
查看CPU个数cat/proc/cpuinfo|grepphysicalid|sort|uniq|wc-l
查看核数cat/proc/cpuinfo|grepcpucores|uniq
二cpu线程数和Java多线程
(1)线程是CPU级别的,单个线程同时只能在单个cpu线程中执行
(2)Java多线程并不是由于cpu线程数为多个才称为多线程,当Java线程数大于cpu线程数,操作系统使用时间片机制,采用线程调度算法,频繁的进行线程切换。
(3)线程是操作系统最小的调度单位,进程是资源(比如:内存)分配的最小单位
(4)Java中的所有线程在JVM进程中,CPU调度的是进程中的线程
线程的调度是指按照特定的机制为多个线程分配CPU的使用权。有两种调度模型:分时调度模型和抢占式调度模型
分时调度模型是指让所有线程轮流获得CPU的使用权,并且平均分配每个线程占用CPU的时间片。
Java虚拟机采用抢占式调度模型,是指优先让可运行池中处于就绪态的线程中优先级高的占用CPU,如果可运行池中线程的优先级相同,那么就随机选择一个线程,使其占用CPU,处于运行状态的线程会一直执行,直至它不得不放弃CPU,一个线程会因为以下原因放弃CPU:
![深化了解线程间值差别](https://tyye.cn/zb_users/theme/tpure/style/images/lazyload.png)
(1)Java虚拟机让当前线程暂时放弃CPU,转到就绪态,使其他线程获得运行机会
(2)当前线程因为某些原因而处于阻塞状态
(3)线程运行结束
Java线程让步:
()方法
就是说当一个线程使用了这个方法之后,它就会把自己CPU执行的时间让掉,让自己或者其它的线程运行,注意是让自己或者其他线程运行(根据CPU的调度),并不是单纯的让给其他线程。
4.等待其他线程结束:join()
当前运行的线程可以调用另一个线程的join()方法,当前运行的线程将转到阻塞状态,直至另一个线程运行结束,它才会恢复运行(阻塞恢复到就绪)
多进程和多线程有什么区别?
线程和进程都是现在电脑概念里比较时髦的用语,什么是多线程,什么是多进程?本文详细的给您介绍一下,希望能增进您对当代电脑技术的了解,有不到之处,还往高手予以更正。 进程(英语:Process,中国大陆译作进程,台湾译作行程)是计算机中已运行程序的实体。 进程本身不会运行,是线程的容器。 程序本身只是指令的集合,进程才是程序(那些指令)的真正运行。 若干进程有可能与同一个程序相关系,且每个进程皆可以同步(循序)或不同步(平行)的方式独立运行。 进程为现今分时系统的基本运作单位线程(英语:thread,台湾译为运行绪),操作系统技术中的术语,是操作系统能够进行运算调度的最小单位。 它被包涵在进程之中,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 在UnixSystemV及SunOS中也被称为轻量进程(lightweightprocesses),但轻量进程更多指内核线程(kernelthread),而把用户线程(userthread)称为线程。 线程是独立调度和分派的基本单位。 线程可以操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如LinuxPortableThread;或者由内核与用户进程,如Windows7的线程,进行混合调度。 同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。 但同一进程中的多个线程有各自的调用栈(callstack),自己的寄存器环境(registercontext),自己的线程本地存储(thread-localstorage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。 在多核或多CPU,或支持Hyper-threading的CPU上使用多线程程序设计的好处是显而易见,即提高了程序的执行吞吐率。 在单CPU单核的计算机上,使用多线程技术,也可以把进程中负责IO处理、人机交互而常备阻塞的部分与密集计算的部分分开来执行,编写专门的workhorse线程执行密集计算,从而提高了程序的执行效率进程是资源分配的最小单位,线程是CPU调度的最小单位。 线程和进程的区别在于,子进程和父进程有不同的代码和数据空间,而多个线程则共享数据空间,每个线程有自己的执行堆栈和程序计数器为其执行上下文.多线程主要是为了节约CPU时间,发挥利用,根据具体情况而定.线程的运行中需要使用计算机的内存资源和CPU。 多进程:进程是程序在计算机上的一次执行活动。 当你运行一个程序,你就启动了一个进程。 显然,程序是死的(静态的),进程是活的(动态的)。 进程可以分为系统进程和用户进程。 凡是用于完成操作系统的各种功能的进程就是系统进程,它们就是处于运行状态下的操作系统本身;所有由用户启动的进程都是用户进程。 进程是操作系统进行资源分配的单位。 进程又被细化为线程,也就是一个进程下有多个能独立运行的更小的单位。 在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态,这便是多任务。 现代的操作系统几乎都是多任务操作系统,能够同时管理多个进程的运行。 多任务带来的好处是明显的,比如你可以边听mp3边上网,与此同时甚至可以将下载的文档打印出来,而这些任务之间丝毫不会相互干扰。 那么这里就涉及到并行的问题,俗话说,一心不能二用,这对计算机也一样,原则上一个CPU只能分配给一个进程,以便运行这个进程。 我们通常使用的计算机中只有一个CPU,也就是说只有一颗心,要让它一心多用,同时运行多个进程,就必须使用并发技术。 实现并发技术相当复杂,最容易理解的是“时间片轮转进程调度算法”,它的思想简单介绍如下:在操作系统的管理下,所有正在运行的进程轮流使用CPU,每个进程允许占用CPU的时间非常短(比如10毫秒),这样用户根本感觉不出来CPU是在轮流为多个进程服务,就好象所有的进程都在不间断地运行一样。 但实际上在任何一个时间内有且仅有一个进程占有CPU。 如果一台计算机有多个CPU,情况就不同了,如果进程数小于CPU数,则不同的进程可以分配给不同的CPU来运行,这样,多个进程就是真正同时运行的,这便是并行。 但如果进程数大于CPU数,则仍然需要使用并发技术。 进行CPU分配是以线程为单位的,一个进程可能由多个线程组成,这时情况更加复杂,但简单地说,有如下关系:总线程数<=CPU数量:并行运行总线程数>CPU数量:并发运行并行运行的效率显然高于并发运行,所以在多CPU的计算机中,多任务的效率比较高。 但是,如果在多CPU计算机中只运行一个进程(线程),就不能发挥多CPU的优势。 这里涉及到多任务操作系统的问题,多任务操作系统(如Windows)的基本原理是:操作系统将CPU的时间片分配给多个线程,每个线程在操作系统指定的时间片内完成(注意,这里的多个线程是分属于不同进程的).操作系统不断的从一个线程的执行切换到另一个线程的执行,如此往复,宏观上看来,就好像是多个线程在一起执行.由于这多个线程分属于不同的进程,因此在我们看来,就好像是多个进程在同时执行,这样就实现了多任务多线程:在计算机编程中,一个基本的概念就是同时对多个任务加以控制。 许多程序设计问题都要求程序能够停下手头的工作,改为处理其他一些问题,再返回主进程。 可以通过多种途径达到这个目的。 最开始的时候,那些掌握机器低级语言的程序员编写一些“中断服务例程”,主进程的暂停是通过硬件级的中断实现的。 尽管这是一种有用的方法,但编出的程序很难移植,由此造成了另一类的代价高昂问题。 中断对那些实时性很强的任务来说是很有必要的。 但对于其他许多问题,只要求将问题划分进入独立运行的程序片断中,使整个程序能更迅速地响应用户的请求。 最开始,线程只是用于分配单个处理器的处理时间的一种工具。 但假如操作系统本身支持多个处理器,那么每个线程都可分配给一个不同的处理器,真正进入“并行运算”状态。 从程序设计语言的角度看,多线程操作最有价值的特性之一就是程序员不必关心到底使用了多少个处理器。 程序在逻辑意义上被分割为数个线程;假如机器本身安装了多个处理器,那么程序会运行得更快,毋需作出任何特殊的调校。 根据前面的论述,大家可能感觉线程处理非常简单。 但必须注意一个问题:共享资源!如果有多个线程同时运行,而且它们试图访问相同的资源,就会遇到一个问题。 举个例子来说,两个线程不能将信息同时发送给一台打印机。 为解决这个问题,对那些可共享的资源来说(比如打印机),它们在使用期间必须进入锁定状态。 所以一个线程可将资源锁定,在完成了它的任务后,再解开(释放)这个锁,使其他线程可以接着使用同样的资源。 多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。 线程是在同一时间需要完成多项任务的时候实现的。 一个采用了多线程技术的应用程序可以更好地利用系统资源。 其主要优势在于充分利用了CPU的空闲时间片,可以用尽可能少的时间来对用户的要求做出响应,使得进程的整体运行效率得到较大提高,同时增强了应用程序的灵活性。 更为重要的是,由于同一进程的所有线程是共享同一内存,所以不需要特殊的数据传送机制,不需要建立共享存储区或共享文件,从而使得不同任务之间的协调操作与运行、数据的交互、资源的分配等问题更加易于解决。 进程间通信(IPC,Inter-ProcessCommunication),指至少两个进程或线程间传送数据或信号的一些技术或方法。 进程是计算机系统分配资源的最小单位。 每个进程都有自己的一部分独立的系统资源,彼此是隔离的。 为了能使不同的进程互相访问资源并进行协调工作,才有了进程间通信。 这些进程可以运行在同一计算机上或网络连接的不同计算机上。 进程间通信技术包括消息传递、同步、共享内存和远程过程调用。
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。