当前位置:首页 » 编程语言 » c语言标准库线程

c语言标准库线程

发布时间: 2022-08-18 19:46:52

1. c语言多线程的概念

线程:线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。
多线程:多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
C语言的开始设计,并未设计多线程的机制,由于随着软硬件的发展及需求的发展。后来C语言才开发了线程库以支持多线程的操作、应用。
主要基于linux介绍C多线程。在编译C的多线程时候,一方面必须指定Linux C语言线程库多线程库pthread,才可以正确编译(例如:gcc test.c -o test -lpthread);另一方面要包含有关线程头文件#include <pthread.h>。

2. c语言怎么创建线程和使用

进程的生命周期:
[1].创建 --- fork
[2].执行 --- a. exec
b.子进程实现代码逻辑
[3].结束 --- exit _exit
僵尸态进程---wait waitpid
孤儿进程
--------------------------------------
进程存在的问题:
(1).进程的创建 --- 复制
(时间 和 空间的开销很大)
(2).进程的运行 --- 调度-->

3. 对于C/C++标准库的线程安全的一个疑问.该怎么处理

c++和c本身都没有标准多线程实现。

但是各个厂家都扩展了c,c++,例如微软就有自己的多线程函数库。

如果没有标准库的话,为什么不把这样重要的内容加入到标准中来呢?
c的发明很早,后续开发c编译器的厂家只能发展自己的扩展库,你不能说你自己的扩展库就是一个标准,因为那是和版权问题相关的。

4. C语言如何实现多线程同时运行

1、点击菜单栏的“Project”选项卡,下拉列表的最后一项“Project options...”是对当前工程的的属性进行设置的。

5. C语言编译器是否支持多线程

C语言编译器有很多,只要不是太旧的都支持多线程。C语言本身是没有多线程的概念的,都是操作系统提供支持,所以不同操作系统中的多线程实现起来可能完全不一样。

6. C语言多线程的操作步骤

线程创建
函数原型:intpthread_create(pthread_t*restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void),void *restrict arg);
返回值:若是成功建立线程返回0,否则返回错误的编号。
形式参数:pthread_t*restrict tidp要创建的线程的线程id指针;const pthread_attr_t *restrict attr创建线程时的线程属性;void *(start_rtn)(void)返回值是void类型的指针函数;void *restrict arg start_rtn的形参。
线程挂起:该函数的作用使得当前线程挂起,等待另一个线程返回才继续执行。也就是说当程序运行到这个地方时,程序会先停止,然后等线程id为thread的这个线程返回,然后程序才会断续执行。
函数原型:intpthread_join(pthread_tthread, void **value_ptr);
参数说明如下:thread等待退出线程的线程号;value_ptr退出线程的返回值。
返回值:若成功,则返回0;若失败,则返回错误号。
线程退出
函数原型:voidpthread_exit(void *rval_ptr);
获取当前线程id
函数原型:pthread_tpthread_self(void);
互斥锁
创建pthread_mutex_init;销毁pthread_mutex_destroy;加锁pthread_mutex_lock;解锁pthread_mutex_unlock。
条件锁
创建pthread_cond_init;销毁pthread_cond_destroy;触发pthread_cond_signal;广播pthread_cond_broadcast;等待pthread_cond_wait。

7. C语言多线程输出不正常求高手指点

线程参数,传地址使用动态分配吧,你只用一个buffer,后面的很可能就把前面的给覆盖了。

把所有的输出printf使用一个互斥的信号量同步一下就行了吧,C的标准库不是线程安全的。

8. C语言怎样实现多线程

首先你要有控制蛇移动方向的全局变量(定义在main以外因为线程函数也要调用它,每次键盘输入都会修改它的值), 比如 char direction 'a' ==左 'w' == 右 'd'==上 's' == 下,然后你在移动时应该是在while里面操作的吧,你每移动一步前都读一下direction这个变量的数值然后再控制移动方向(注意s这个键可以忽略因为不会倒着走) 然后你可以用pthread.h这个库 例子是 pthread t;// 定义一个线程 pthread_create(&t, null, listen_keyboard_input, null);//建立线程执行listen_keyboard_input这个函数 这个线程执行的函数 void listen_keyboard_input(){ while(应该通过某个信号来退出这个循环,从而表示游戏结束){ direction =getchar(); } } 但是这里存在同步问题, 比如当这个线程的getchar()在给direction辅助的同时,你控制贪吃蛇移动的线程正在调用 direction的值来判断下一个移动方向,这就会出问题,所以要加一个锁,叫 mutex lock;这个也定义成全局变量可以使各线程共享。 pthread_mutex_t mutex; //定义一个锁 pthread_mutex_init(&mutex, null, null);//初始化 然后把函数修改成 void listen_keyboard_input(){ while(应该通过某个信号来退出这个循环,从而表示游戏结束){ pthread_mutex_lock(&mutex); direction =getchar(); pthread_mutex_unlock(&mutex); } } 另外一个控制贪吃蛇移动的时候也要加锁 while(.....){ char c; pthread_mutex_lock(&mutex); c = direction; pthread_mutex_unlock(&mutex); switch(c){ ................ } ................................... } 这样就好了 注意你的控制贪吃蛇移动的部分也必须要放在另外一个pthread 里面执行,如果放在主线程, 主线程会一直等listen_keyboard_input而什么事都不会做 你把这两个线程用 pthread_create 创建完成后 用 t1.join(); t2.join(); 就可以使这两个线程并发执行了 如果你用的是linux 来编译的,你再输入gcc 指令后加上 -lpthread 就可以了 还有什么不懂的你可以多找找 pthread 类的例子

9. win32程序创建线程用c语言库的_beginthread还是API的CreateThread哪种用的多

让我们简单回顾一下历史。很早以前,是一个库用于单线程应用程序,另一个库用于多线程应
用程序。之所以采用这个设计,是由于标准C运行库是在1970年左右发明的。要在很久很久之
后,才会在操作系统上出现线程的概念。标准C运行库的发明者根本没有考虑到为多线程应用
程序使用C运行库的问题。让我们用一个例子来了解可能遇到的问题。
以标准C运行库的全局变量errno为例。有的函数会在出错时设置该变量。假定现在有这样的一
个代码段:
BOOL fFailure = (system("NOTEPAD.EXE README.TXT") == -1);
if (fFailure) {
switch (errno) {
case E2BIG: // Argument list or environment too big
break;
case ENOENT: // Command interpreter cannot be found
break;
case ENOEXEC: // Command interpreter has bad format
break;
case ENOMEM: // Insufficient memory to run command
break;
}
}
假设在调用了system函数之后,并在执行if语句之前,执行上述代码的线程被中断了。另外还假
设,这个线程被中断后,同一个进程中的另一个线程开始执行,而且这个新线程将执行另一个
C运行库函数,后者设置了全局变量errno。当CPU后来被分配回第一个线程时,对于上述代码
中的system函数调用,errno反映的就不再是正确的错误码。为了解决这个问题,每个线程都需
要它自己的errno变量。此外,必须有某种机制能够让一个线程引用它自己的errno变量,同时
不能让它去碰另一个线程的errno变量。
这仅仅是证明了“标准C/C++运行库最初不是为多线程应用程序而设计”的众多例子中的一个。
在多线程环境中会出问题的C/C++运行库变量和函数有errno,_doserrno,strtok,_wcstok,
strerror,_strerror,tmpnam,tmpfile,asctime,_wasctime,gmtime,_ecvt和_fcvt等等。
为了保证C和C++多线程应用程序正常运行,必须创建一个数据结构,并使之与使用了C/C++运
行库函数的每个线程关联。然后,在调用C/C++运行库函数时,那些函数必须知道去查找主调
线程的数据块,从而避免影响到其他线程。
那么,系统在创建新的线程时,是如何知道要分配这个数据块的呢?答案是它并不知道。系统
并不知道应用程序是用C/C++来写的,不知道你调用的函数并非天生就是线程安全的。保证线
程安全是程序员的责任。创建新线程时,一定不要调用操作系统的CreateThread函数。相反,
必须调用C/C++运行库函数_beginthreadex:
unsigned long _beginthreadex(
void *security,
unsigned stack_size,
unsigned (*start_address)(void *),
void *arglist,
unsigned initflag,
unsigned *thrdaddr);
_beginthreadex函数的参数列表与CreateThread函数的一样,但是参数名称和类型并不完全一
样。这是因为Microsoft的C/C++运行库开发组认为,C/C++运行库函数不应该对Windows数据类
型有任何依赖。_beginthreadex函数也会返回新建线程的句柄,就像CreateThread那样。所以,
如果已经在自己的源代码中调用了CreateThread函数,可以非常方便地用_beginthreadex来全
局替换所有CreateThread。但是,由于数据类型并不完相同,所以可能还必须执行一些类型转
换,以便顺利地通过编译。为了简化这个工作,我创建了一个名为chBEGINTHREADEX的宏,
并在自己的源代码中使用:
typedef unsigned (__stdcall *PTHREAD_START) (void *);
#define chBEGINTHREADEX(psa, cbStack, pfnStartAddr, \
pvParam, fdwCreate, pdwThreadID) \
((HANDLE) _beginthreadex( \
(void *) (psa), \
(unsigned) (cbStackSize), \
(PTHREAD_START) (pfnStartAddr), \
(void *) (pvParam), \
(unsigned) (dwCreateFlags), \
(unsigned *) (pdwThreadID)))
根据Microsoft为C/C++运行库提供的源代码,很容易看出_beginthreadex能而CreateThread不能
做的事情。事实上,在搜索了Visual Studio安装文件夹后,我在<Program Files>\Microsoft Visual
Studio 8\VC\crt\src\Threadex.c中找到了_beginthreadex的源代码。为节省篇幅,这里没有全部照
抄一遍。相反,我在这里提供了该函数的伪代码版本,强调了其中最有意思的地方:
uintptr_t __cdecl _beginthreadex (
void *psa,
unsigned cbStackSize,
unsigned (__stdcall * pfnStartAddr) (void *),
void * pvParam,
unsigned dwCreateFlags,
unsigned *pdwThreadID) {
_ptiddata ptd; // Pointer to thread's data block
uintptr_t thdl; // Thread's handle
// Allocate data block for the new thread.
if ((ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) == NULL)
goto error_return;
// Initialize the data block.
initptd(ptd);
// Save the desired thread function and the parameter
// we want it to get in the data block.
ptd->_initaddr = (void *) pfnStartAddr;
ptd->_initarg = pvParam;
ptd->_thandle = (uintptr_t)(-1);
// Create the new thread.
thdl = (uintptr_t) CreateThread((LPSECURITY_ATTRIBUTES)psa, cbStackSize,
_threadstartex, (PVOID) ptd, dwCreateFlags, pdwThreadID);
if (thdl == 0) {
// Thread couldn't be created, cleanup and return failure.
goto error_return;
}
// Thread created OK, return the handle as unsigned long.
return(thdl);
error_return:
// Error: data block or thread couldn't be created.
// GetLastError() is mapped into errno corresponding values
// if something wrong happened in CreateThread.
_free_crt(ptd);
return((uintptr_t)0L);
}
对于_beginthreadex函数,以下几点需要重点关注。
􀂾 每个线程都有自己的专用_tiddata内存块,它们是从C/C++运行库的堆(heap)上分配
的。
􀂾 传给_beginthreadex的线程函数的地址保存在_tiddata内存块中。(_tiddata结构在
Mtdll.h文件的C++源代码中。)纯粹是为了增加趣味性,我在下面重现了这个结构。要
传入_beginthreadex函数的参数也保存在这个数据块中。
􀂾 _beginthreadex确实会在内部调用CreateThread,因为操作系统只知道用这种方式来
创建一个新线程。
􀂾 CreateThread函数被调用时,传给它的函数地址是_threadstartex(而非
pfnStartAddr)。另外,参数地址是_tiddata结构的地址,而非pvParam。
􀂾 如果一切顺利,会返回线程的句柄,就像CreateThread那样。任何操作失败,会返回0。
struct _tiddata {
unsigned long _tid; /* thread ID */
unsigned long _thandle; /* thread handle */
int _terrno; /* errno value */
unsigned long _tdoserrno; /* _doserrno value */
unsigned int _fpds; /* Floating Point data segment */
unsigned long _holdrand; /* rand() seed value */
char* _token; /* ptr to strtok() token */
wchar_t* _wtoken; /* ptr to wcstok() token */
unsigned char* _mtoken; /* ptr to _mbstok() token */
/* following pointers get malloc'd at runtime */
char* _errmsg; /* ptr to strerror()/_strerror() buff */
wchar_t* _werrmsg; /* ptr to _wcserror()/__wcserror() buff */
char* _namebuf0; /* ptr to tmpnam() buffer */
wchar_t* _wnamebuf0; /* ptr to _wtmpnam() buffer */
char* _namebuf1; /* ptr to tmpfile() buffer */
wchar_t* _wnamebuf1; /* ptr to _wtmpfile() buffer */
char* _asctimebuf; /* ptr to asctime() buffer */
wchar_t* _wasctimebuf; /* ptr to _wasctime() buffer */
void* _gmtimebuf; /* ptr to gmtime() structure */
char* _cvtbuf; /* ptr to ecvt()/fcvt buffer */
unsigned char _con_ch_buf[MB_LEN_MAX];
/* ptr to putch() buffer */
unsigned short _ch_buf_used; /* if the _con_ch_buf is used */
/* following fields are needed by _beginthread code */
void* _initaddr; /* initial user thread address */
void* _initarg; /* initial user thread argument */
/* following three fields are needed to support signal handling and runtime errors */
void* _pxcptacttab; /* ptr to exception-action table */
void* _tpxcptinfoptrs;/* ptr to exception info pointers */
int _tfpecode; /* float point exception code */
/* pointer to the of the multibyte character information used by the thread */
pthreadmbcinfo ptmbcinfo;
/* pointer to the of the locale information used by the thread */
pthreadlocinfo ptlocinfo;
int _ownlocale; /* if 1, this thread owns its own locale */
/* following field is needed by NLG routines */
unsigned long _NLG_dwCode;
/*
* Per-Thread data needed by C++ Exception Handling
*/
void* _terminate; /* terminate() routine */
void* _unexpected; /* unexpected() routine */
void* _translator; /* S.E. translator */
void* _purecall; /* called when pure virtual happens */
void* _curexception; /* current exception */
void* _curcontext; /* current exception context */
int _ProcessingThrow; /* for uncaught_exception */
void* _curexcspec; /* for handling exceptions thrown from std::unexpected */
#if defined (_M_IA64) || defined (_M_AMD64)
void* _pExitContext;
void* _pUnwindContext;
void* _pFrameInfoChain;
unsigned __int64 _ImageBase;
#if defined (_M_IA64)
unsigned __int64 _TargetGp;
#endif /* defined (_M_IA64) */
unsigned __int64 _ThrowImageBase;
void* _pForeignException;
#elif defined (_M_IX86)
void* _pFrameInfoChain;
#endif /* defined (_M_IX86) */
_setloc_struct _setloc_data;
void* _encode_ptr; /* EncodePointer() routine */
void* _decode_ptr; /* DecodePointer() routine */
void* _reserved1; /* nothing */
void* _reserved2; /* nothing */
void* _reserved3; /* nothing */
int _ cxxReThrow; /* Set to True if it's a rethrown C++ Exception */
unsigned long __initDomain; /* initial domain used by _beginthread[ex] for managed
function */
};
typedef struct _tiddata * _ptiddata;
为新线程分配并初始化_tiddata结构之后,接着应该知道这个结构是如何与线程关联的。来看看
_threadstartex函数(它也在C/C++运行库的Threadex.c文件中)。下面是我为这个函数及其helper
函数__callthreadstartex编写的伪代码版本:
static unsigned long WINAPI _threadstartex (void* ptd) {
// Note: ptd is the address of this thread's tiddata block.
// Associate the tiddata block with this thread so
// _getptd() will be able to find it in _callthreadstartex.
TlsSetValue(__tlsindex, ptd);
// Save this thread ID in the _tiddata block.
((_ptiddata) ptd)->_tid = GetCurrentThreadId();
// Initialize floating-point support (code not shown).
// call helper function.
_callthreadstartex();
// We never get here; the thread dies in _callthreadstartex.
return(0L);
}
static void _callthreadstartex(void) {
_ptiddata ptd; /* pointer to thread's _tiddata struct */
// get the pointer to thread data from TLS
ptd = _getptd();
// Wrap desired thread function in SEH frame to
// handle run-time errors and signal support.
__try {
// Call desired thread function, passing it the desired parameter.
// Pass thread's exit code value to _endthreadex.
_endthreadex(
( (unsigned (WINAPI *)(void *))(((_ptiddata)ptd)->_initaddr) )
( ((_ptiddata)ptd)->_initarg ) ) ;
}
__except(_XcptFilter(GetExceptionCode(), GetExceptionInformation())){
// The C run-time's exception handler deals with run-time errors
// and signal support; we should never get it here.
_exit(GetExceptionCode());
}
}
关于_threadstartex函数,要注意以下重点:
􀂾 新的线程首先执行RtlUserThreadStart (在NTDLL.dll文件中),然后再跳转到
_threadstartex。
􀂾 _threadstartex惟一的参数就是新线程的_tiddata内存块的地址。
􀂾 TlsSetValue是一个操作系统函数,它将一个值与主调线程关联起来。这就是所谓的线
程本地存储(Thread Local Storage,TLS),详情参见第21章。_threadstartex函数将
_tiddata内存块与新建线程关联起来。
􀂾 在无参数的helper函数_callthreadstartex中,一个SEH帧将预期要执行的线程函数包围
起来。这个帧处理着与运行库有关的许多事情——比如运行时错误(如抛出未被捕捉的
C++异常)——和C/C++运行库的signal函数。这一点相当重要。如果用CreateThread函
数新建了一个线程,然后调用C/C++运行库的signal函数,那么signal函数不能正常工作。
􀂾 预期要执行的线程函数会被调用,并向其传递预期的参数。前面讲过,函数的地址和
参数由_beginthreadex保存在TLS的_tiddata数据块中;并会在_callthreadstartex中从
TLS中获取。
􀂾 线程函数的返回值被认为是线程的退出代码。
注意_callthreadstartex不是简单地返回到_threadstartex,继而到RtlUserThreadStart;
如果是那样的话,线程会终止运行,其退出代码也会被正确设置,但线程的_tiddata
内存块不会被销毁。这会导致应用程序出现内存泄漏。为防止出现这个问题,会调用
_endthreadex(也是一个C/C++运行库函数),并向其传递退出代码。
最后一个需要关注的函数是_endthreadex(也在C运行库的Threadex.c文件中)。下面是我编写的该
函数的伪代码版本:
void __cdecl _endthreadex (unsigned retcode) {
_ptiddata ptd; // Pointer to thread's data block
// Clean up floating-point support (code not shown).
// Get the address of this thread's tiddata block.
ptd = _getptd_noexit ();
// Free the tiddata block.
if (ptd != NULL)
_freeptd(ptd);
// Terminate the thread.
ExitThread(retcode);
}
对于_endthreadex函数,要注意几下几点:
􀂾 C运行库的_getptd_noexit函数在内部调用操作系统的TlsGetValue函数,后者获取主调
线程的tiddata内存块的地址。
􀂾 然后,此数据块被释放,调用操作系统的ExitThread函数来实际地销毁线程。当然,
退出代码会被传递,并被正确地设置。
在本章早些时候,我曾建议大家应该避免使用ExitThread函数。这是千真万确的,而且我在这
里并不打算自相矛盾。前面说过,此函数会杀死主调线程,而且不允许它从当前执行的函数返
回。由于函数没有返回,所以构造的任何C++对象都不会被析构。现在,我们又有了不调用
ExitThread函数的另一个理由:它会阻止线程的_tiddata内存块被释放,使应用程序出现内存泄
漏(直到整个进程终止)。
Microsoft的C++开发团队也意识到,总有一些开发人员喜欢调用ExitThread。所以,他们必须
使这成为可能,同时尽可能避免应用程序出现内存泄漏的情况。如果真的想要强行杀死自己的
线程,可以让它调用_endthreadex(而不是ExitThread)来释放线程的_tiddata块并退出。不过,
我并不鼓励你调用_endthreadex。
现在,你应该理解了C/C++运行库函数为什么要为每一个新线程准备一个独立的数据块,而且
应该理解了_beginthreadex如何分配和初始化此数据块,并将它与新线程关联起来。另外,你还
应理解了_endthreadex函数在线程终止运行时是如何释放该数据块的。
一旦这个数据块被初始化并与线程关联,线程调用的任何需要“每线程实例数据”的C/C++运
行库函数都可以轻易获取主调线程的数据块的地址(通过TlsGetValue),并操纵线程的数据。这
对函数来说是没有问题的。但是,对于errno之类的全局变量,它又是如何工作的呢?errno是在
标准C headers中定义的,如下所示:
_CRTIMP extern int * __cdecl _errno(void);
#define errno (*_errno())
int* __cdecl _errno(void) {
_ptiddata ptd = _getptd_noexit();
if (!ptd) {
return &ErrnoNoMem;
} else {
return (&ptd->_terrno);
}
}
任何时候引用errno,实际都是在调用内部的C/C++运行库函数_errno。该函数将地址返回给“与
主调线程关联的数据块”中的errno数据成员。注意,errno宏被定义为获取该地址的内容。这
个定义是必要的,因为很可能写出下面这样的代码:
int *p = &errno;
if (*p == ENOMEM) {
...
}
如果内部函数_errno只是返回errno的值,上述代码将不能通过编译。
C/C++运行库还围绕特定的函数放置了同步原语(synchronization primitives)。例如,如果两个
线程同时调用malloc,堆就会损坏。C/C++运行库函数阻止两个线程同时从内存堆中分配内存。
具体的办法是让第2个线程等待,直至第1个线程从malloc函数返回。然后,才允许第2个线程进
入。(线程同步将在第8章和第9章详细讨论。)显然,所有这些额外的工作影响了C/C++运行库的
多线程版本的性能。
C/C++运行库函数的动态链接版本被写得更加泛化,使其可以被使用了C/C++运行库函数的所有
运行的应用程序和DLL共享。因此,库只有一个多线程版本。由于C/C++运行库是在一个DLL
中提供的,所以应用程序(.exe文件)和DLL不需要包含C/C++运行库函数的代码,所以可以更小
一些。另外,如果Microsoft修复了C/C++运行库DLL的任何bug,应用程序将自动获得修复。
就像你期望的一样,C/C++运行库的启动代码为应用程序的主线程分配并初始化了一个数据块。
这样一来,主线程就可以安全地调用任何C/C++运行库函数。当主线程从其入口函数返回的时
候,C/C++运行库函数会释放关联的数据块。此外,启动代码设置了正确的结构化异常处理代
码,使主线程能成功调用C/C++运行库的signal函数。
6.7.1 用_beginthreadex 而不要用CreateThread 创建线程
你可能会好奇,假如调用CreateThread而不是C/C++运行库的_beginthreadex来创建新线程,会
发生什么呢?当一个线程调用一个需要_tiddata结构的C/C++运行库函数时,会发生下面的情
况。(大多数C/C++运行库函数都是线程安全的,不需要这个结构。)首先,C/C++运行库函数尝
试取得线程数据块的地址(通过调用TlsGetValue)。如果NULL被作为_tiddata块的地址返回,表
明主调线程没有与之关联的_tiddata块。在这个时候,C/C++运行库函数会为主调线程分配并初
始化一个_tiddata块。然后,这个块会与线程关联(通过TlsSetValue) ,而且只要线程还在运行,
这个块就会一直存在并与线程关联。现在,C/C++运行库函数可以使用线程的_tiddata块,以后
调用的任何C/C++运行库函数也都可以使用。
当然,这是相当诱人的,因为线程(几乎)可以顺畅运行。但事实上,问题还是有的。第一个
问题是,假如线程使用了C/C++运行库的signal函数,则整个进程都会终止,因为结构化异常处
理(SEH)帧没有就绪。第二个问题是,假如线程不是通过调用_endthreadex来终止的,数据
块就不能被销毁,从而导致内存泄漏。(对于一个用CreateThread函数来创建的线程,谁会调用
_endthreadex呢?)

10. 标准C/C++包不包含socket相关和线程之类的函数

不包含
C++标准程序库里面没有socket的相关支持
这些支持要依靠具体的C/C++版本提供的库来支持
另外,线程方面,BOOST库(很可能成为下一个版本的c++标准的一部分)有支持

热点内容
车辆办理解压要收费怎么处理 发布:2025-01-19 18:39:46 浏览:950
san存储柜 发布:2025-01-19 18:39:35 浏览:63
伏魔记安卓版怎么什么键都没有 发布:2025-01-19 18:38:54 浏览:901
lol声音文件夹 发布:2025-01-19 18:38:54 浏览:317
锐程cc尊贵版都有哪些配置 发布:2025-01-19 18:27:08 浏览:977
php手机网站 发布:2025-01-19 18:26:34 浏览:456
一加存储路径 发布:2025-01-19 18:21:01 浏览:446
sqlserver误删 发布:2025-01-19 18:21:00 浏览:649
球球安全中心动态密码在哪里 发布:2025-01-19 18:20:23 浏览:790
python读取config文件 发布:2025-01-19 18:15:10 浏览:458