-
vc 如何创建线程
- 时间:2024-11-23 08:08:53
大家好,今天Win10系统之家小编给大家分享「vc 如何创建线程」的知识,如果能碰巧解决你现在面临的问题,记得收藏本站或分享给你的好友们哟~,现在开始吧!
1.VC++ 如何创建一个线程并传递参数
展开全部
近来做个项目需要写点程序,比较烦.net写的老是需要背着.net包走,所以准备用VC++来写,其中用到了线程方面的知识这里记录下来以备后用。 创建一个工作线程十分简单,只需要两步你的线程就能跑了:(1)实现线程函数和(2)开始线程。不需要由CWinThread派生类,你可以不加修改地使用CWinThread。下面我们来看看如何开始一个线程。
AfxBeginThread有两种形式,一种是用来创建用户界面线程的,另一种就是用来创建工作线程的。为了开始执行你的线程,只需要向AfxBeginThread提供下面的参数就可以了:
线程函数的地址
传送到线程函数的参数
(可选的)线程的优先级,默认的是平常的优先级,如果希望使用其它优先级请参阅::SetThreadPriority
(可选的)线程的堆栈大小,默认的大小是和创建线程的堆栈一样大
(可选的)如果用户创建的线程在开始的时候在挂起态,而不在运行态,可以设置为CREATE_SUSPENDED
(可选的)线程的安全属性,默认的是和父线程的访问权限一样,有关安全信息的格式,请参阅SECURITY_ATTRIBUTES
AfxBeginThread为用户创建并初始化一个CWinThread对象,运行这个对象,并返回它的地址,这样通过这个地址用户就可以找到它了。在这一过程中还要进行许多检查。这一切都不用你操心。那么下面我们来看看线程函数怎么写。线程函数定义了线程要做什么,在进入这个函数的时候线程开始,退出的时候线程结束。这个函数必须是下面的形式:
UINT MyControllingFunction( LPVOID pParam );
参数是一个32位数,这个参数在线程对象创建时传送给对象的构造函数。至于线程函数要怎么处理这个数,那就随便了,它可能是一个人的年纪,可能是一个文件的地址,可能是一个窗口句柄,反正你想它是什么就是什么,主动权在你手里。如果参数指的是一个结构,结束可以用来向线程传送参数,也可以让线程把结果传回主程序,线程需要通知主程序,什么时候来取结果。
在线程函数结束时,应该返回一个UINT类型的值,说明返回原因,也就是返回代码。通常这个数为0,表示正常返回,当然你也可以定义一个错误编码指示错误了。至于什么数代表什么,全在你。下面是一个线程函数的例子,这个例子解释如何定义线程函数,也介绍了如何从程序的其它地方控制线程:(为了了解线程的全部,请安心阅读其它文章)
UINT MyThreadProc( LPVOID pParam ){CMyObject* pObject = (CMyObject*)pParam;
if (pObject == NULL ||!pObject->IsKindOf(RUNTIME_CLASS(CMyObject))) return -1; // illegal parameter
// do something with 'pObject'
return 0; // thread completed successfully}
// inside a different function in the program
AfxBeginThread(MyThreadProc, pNewObject); 上面的例子是MSDN上写的,下面我还写个简单的,主函数传递参数给线程函数,并且线程函数返回值。主函数:void CNEWDLG::CreateNewThread() { CString s="hello"; AfxBeginThead(WorkThread,&s);//这里的参数&s完全可以改成你需要的其他类型的地址,实现你的操作。 Sleep(1000); MessageBox(s,NULL,0);}线程函数:UINT WorkThread(LPVOID pParam) //偶觉得关键是这个参数,它可以传递任何类型。{ CString* s1=(CString*)pParam; //不管你传递的是什么类型,都可以通过这种方式转换后使用。
2.vc如何开新线程
有许多应用程序创建的线程花费了大量时间在睡眠状态来等待事件的发生。还有一些线程进入睡眠状态后定期被唤醒以轮询工作方式来改变或者更新状态信息。线程池可以让你更有效地使用线程,它为你的应用程序提供一个由系统管理的工作者线程池。至少会有一个线程来监听放到线程池的所有等待操作,当等待操作完成后,线程池中将会有一个工作者线程来执行相应的回调函数。
你也可以把没有等待操作的工作项目放到线程池中,用queueuserworkitem函数来完成这个工作,把要执行的工作项目函数通过一个参数传递给线程池。工作项目被放到线程池中后,就不能再取消了。
timer-queuetimers和registeredwaitoperations也使用线程池来实现。他们的回调函数也放在线程池中。你也可以用bindiocompletioncallback函数来投递一个异步io操作,在io完成端口上,回调函数也是由线程池线程来执行。
当第一次调用queueuserworkitem函数或者bindiocompletioncallback函数的时候,线程池被自动创建,或者timer-queuetimers或者registeredwaitoperations放入回调函数的时候,线程池也可以被创建。线程池可以创建的线程数量不限,仅受限于可用的内存,每一个线程使用默认的初始堆栈大小,运行在默认的优先级上。
线程池中有两种类型的线程:io线程和非io线程。io线程等待在可告警状态,工作项目作为apc放到io线程中。如果你的工作项目需要线程执行在可警告状态,你应该将它放到io线程。
非io工作者线程等待在io完成端口上,使用非io线程比io线程效率更高,也就是说,只要有可能的话,尽量使用非io线程。io线程和非io线程在异步io操作没有完成之前都不会退出。然而,不要在非io线程中发出需要很长时间才能完成的异步io请求。
正确使用线程池的方法是,工作项目函数以及它将会调用到的所有函数都必须是线程池安全的。安全的函数不应该假设线程是一次性线程的或者是永久线程。一般来说,应该避免使用线程本地存储和发出需要永久线程的异步io调用,比如说regnotifychangekeyvalue函数。如果需要在永久线程中执行这样的函数的话,可以给queueuserworkitem传递一个选项wt_executeinpersistentthread。
注意,线程池不能兼容com的单线程套间(sta)模型。
为了更深入地讲解操作系统实现的线程池的优越性,我们首先尝试着自己实现一个简单的线程池模型。
代码如下:
/**//************************************************************************/
/**//*testourownthreadpool.*/
/**//************************************************************************/
typedefstruct_thread_pool
{
handlequitevent;
handleworkitemsemaphore;
longworkitemcount;
list_entryworkitemheader;
critical_sectionworkitemlock;
longthreadnum;
handle*threadsarray;
}thread_pool,*pthread_pool;
typedefvoid(*work_item_proc)(pvoidparam);
typedefstruct_work_item
{
list_entrylist;
work_item_procuserproc;
pvoiduserparam;
}work_item,*pwork_item;
dwordwinapiworkerthread(pvoidpparam)
{
pthread_poolpthreadpool=(pthread_pool)pparam;
handleevents[2];
events[0]=pthreadpool->quitevent;
events[1]=pthreadpool->workitemsemaphore;
for(;;)
{
dworddwret=waitformultipleobjects(2,events,false,infinite);
if(dwret==wait_object_0)
break;
//
//executeuser'sproc.
//
elseif(dwret==wait_object_0+1)
{
pwork_itempworkitem;
plist_entryplist;
entercriticalsection(&pthreadpool->workitemlock);
_assert(!islistempty(&pthreadpool->workitemheader));
plist=removeheadlist(&pthreadpool->workitemheader);
leavecriticalsection(&pthreadpool->workitemlock);
pworkitem=containing_record(plist,work_item,list);
pworkitem->userproc(pworkitem->userparam);
interlockeddecrement(&pthreadpool->workitemcount);
free(pworkitem);
}
else
{
_assert(0);
break;
}
}
return0;
}
boolinitializethreadpool(pthread_poolpthreadpool,longthreadnum)
{
pthreadpool->quitevent=createevent(null,true,false,null);
pthreadpool->workitemsemaphore=createsemaphore(null,0,0x7fffffff,null);
pthreadpool->workitemcount=0;
initializelisthead(&pthreadpool->workitemheader);
initializecriticalsection(&pthreadpool->workitemlock);
pthreadpool->threadnum=threadnum;
pthreadpool->threadsarray=(handle*)malloc(sizeof(handle)*threadnum);
for(inti=0;i<threadnum;i++)
{
pthreadpool->threadsarray[i]=createthread(null,0,workerthread,pthreadpool,0,null);
}
returntrue;
}
voiddestroythreadpool(pthread_poolpthreadpool)
{
setevent(pthreadpool->quitevent);
for(inti=0;i<pthreadpool->threadnum;i++)
{
waitforsingleobject(pthreadpool->threadsarray[i],infinite);
closehandle(pthreadpool->threadsarray[i]);
}
free(pthreadpool->threadsarray);
closehandle(pthreadpool->quitevent);
closehandle(pthreadpool->workitemsemaphore);
deletecriticalsection(&pthreadpool->workitemlock);
while(!islistempty(&pthreadpool->workitemheader))
{
pwork_itempworkitem;
plist_entryplist;
plist=removeheadlist(&pthreadpool->workitemheader);
pworkitem=containing_record(plist,work_item,list);
free(pworkitem);
}
}
boolpostworkitem(pthread_poolpthreadpool,work_item_procuserproc,pvoiduserparam)
{
pwork_itempworkitem=(pwork_item)malloc(sizeof(work_item));
if(pworkitem==null)
returnfalse;
pworkitem->userproc=userproc;
pworkitem->userparam=userparam;
entercriticalsection(&pthreadpool->workitemlock);
inserttaillist(&pthreadpool->workitemheader,&pworkitem->list);
leavecriticalsection(&pthreadpool->workitemlock);
interlockedincrement(&pthreadpool->workitemcount);
releasesemaphore(pthreadpool->workitemsemaphore,1,null);
returntrue;
}
voiduserproc1(pvoiddwparam)
{
workitem(dwparam);
}
voidtestsimplethreadpool(boolbwaitmode,longthreadnum)
{
thread_poolthreadpool;
initializethreadpool(&threadpool,threadnum);
completeevent=createevent(null,false,false,null);
begintime=gettickcount();
itemcount=20;
for(inti=0;i<20;i++)
{
postworkitem(&threadpool,userproc1,(pvoid)bwaitmode);
}
waitforsingleobject(completeevent,infinite);
closehandle(completeevent);
destroythreadpool(&threadpool);
}
我们把工作项目放到一个队列中,用一个信号量通知线程池,线程池中任意一个线程取出工作项目来执行,执行完毕之后,线程返回线程池,继续等待新的工作项目。
线程池中线程的数量是固定的,预先创建好的,永久的线程,直到销毁线程池的时候,这些线程才会被销毁。
线程池中线程获得工作项目的机会是均等的,随机的,并没有特别的方式保证哪一个线程具有特殊的优先获得工作项目的机会。
而且,同一时刻可以并发运行的线程数目没有任何限定。事实上,在我们的执行计算任务的演示代码中,所有的线程都并发执行。
下面,我们再来看一下,完成同样的任务,系统提供的线程池是如何运作的。
/**//************************************************************************/
/**//*queueworkitemtest.*/
/**//************************************************************************/
dwordbegintime;
longitemcount;
handlecompleteevent;
intcompute()
{
srand(begintime);
for(inti=0;i<20*1000*1000;i++)
rand();
returnrand();
}
dwordwinapiworkitem(lpvoidlpparameter)
{
boolbwaitmode=(bool)lpparameter;
if(bwaitmode)
sleep(1000);
else
compute();
if(interlockeddecrement(&itemcount)==0)
{
printf("timetotal%dsecond.\n",gettickcount()-begintime);
setevent(completeevent);
}
return0;
}
voidtestworkitem(boolbwaitmode,dwordflag)
{
completeevent=createevent(null,false,false,null);
begintime=gettickcount();
itemcount=20;
for(inti=0;i<20;i++)
{
queueuserworkitem(workitem,(pvoid)bwaitmode,flag);
}
waitforsingleobject(completeevent,infinite);
closehandle(completeevent);
}
很简单,是吧?我们仅需要关注于我们的回调函数即可。但是与我们的简单模拟来比,系统提供的线程池有着更多的优点。
首先,线程池中线程的数目是动态调整的,其次,线程池利用io完成端口的特性,它可以限制并发运行的线程数目,默认情况下,将会限制为cpu的数目,这可以减少线程切换。它挑选最近执行过的线程再次投入执行,从而避免了不必要的线程切换。
3.vc如何创建 线程池
有许多应用程序创建的线程花费了大量时间在睡眠状态来等待事件的发生。还有一些线程进入睡眠状态后定期被唤醒以轮询工作方式来改变或者更新状态信息。线程池可以让你更有效地使用线程,它为你的应用程序提供一个由系统管理的工作者线程池。至少会有一个线程来监听放到线程池的所有等待操作,当等待操作完成后,线程池中将会有一个工作者线程来执行相应的回调函数。
你也可以把没有等待操作的工作项目放到线程池中,用QueueUserWorkItem函数来完成这个工作,把要执行的工作项目函数通过一个参数传递给线程池。工作项目被放到线程池中后,就不能再取消了。
Timer-queue timers和Registered wait operations也使用线程池来实现。他们的回调函数也放在线程池中。你也可以用BindIOCompletionCallback函数来投递一个异步IO操作,在IO完成端口上,回调函数也是由线程池线程来执行。
当第一次调用QueueUserWorkItem函数或者BindIOCompletionCallback函数的时候,线程池被自动创建,或者Timer-queue timers或者Registered wait operations放入回调函数的时候,线程池也可以被创建。线程池可以创建的线程数量不限,仅受限于可用的内存,每一个线程使用默认的初始堆栈大小,运行在默认的优先级上。
线程池中有两种类型的线程:IO线程和非IO线程。IO线程等待在可告警状态,工作项目作为APC放到IO线程中。如果你的工作项目需要线程执行在可警告状态,你应该将它放到IO线程。
非IO工作者线程等待在IO完成端口上,使用非IO线程比IO线程效率更高,也就是说,只要有可能的话,尽量使用非IO线程。IO线程和非IO线程在异步IO操作没有完成之前都不会退出。然而,不要在非IO线程中发出需要很长时间才能完成的异步IO请求。
正确使用线程池的方法是,工作项目函数以及它将会调用到的所有函数都必须是线程池安全的。安全的函数不应该假设线程是一次性线程的或者是永久线程。一般来说,应该避免使用线程本地存储和发出需要永久线程的异步IO调用,比如说RegNotifyChangeKeyValue函数。如果需要在永久线程中执行这样的函数的话,可以给QueueUserWorkItem传递一个选项WT_EXECUTEINPERSISTENTTHREAD。
注意,线程池不能兼容COM的单线程套间(STA)模型。
为了更深入地讲解操作系统实现的线程池的优越性,我们首先尝试着自己实现一个简单的线程池模型。
代码如下:
/**//************************************************************************/
/**//* Test Our own thread pool. */
/**//************************************************************************/
typedef struct _THREAD_POOL
{
HANDLE QuitEvent;
HANDLE WorkItemSemaphore;
LONG WorkItemCount;
LIST_ENTRY WorkItemHeader;
CRITICAL_SECTION WorkItemLock;
LONG ThreadNum;
HANDLE *ThreadsArray;
}THREAD_POOL, *PTHREAD_POOL;
typedef VOID (*WORK_ITEM_PROC)(PVOID Param);
typedef struct _WORK_ITEM
{
LIST_ENTRY List;
WORK_ITEM_PROC UserProc;
PVOID UserParam;
}WORK_ITEM, *PWORK_ITEM;
DWORD WINAPI WorkerThread(PVOID pParam)
{
PTHREAD_POOL pThreadPool = (PTHREAD_POOL)pParam;
HANDLE Events[2];
Events[0] = pThreadPool->QuitEvent;
Events[1] = pThreadPool->WorkItemSemaphore;
for(;;)
{
DWORD dwRet = WaitForMultipleObjects(2, Events, FALSE, INFINITE);
if(dwRet == WAIT_OBJECT_0)
break;
//
// execute user's proc.
//
else if(dwRet == WAIT_OBJECT_0 +1)
{
PWORK_ITEM pWorkItem;
PLIST_ENTRY pList;
EnterCriticalSection(&pThreadPool->WorkItemLock);
_ASSERT(!IsListEmpty(&pThreadPool->WorkItemHeader));
pList = RemoveHeadList(&pThreadPool->WorkItemHeader);
LeaveCriticalSection(&pThreadPool->WorkItemLock);
pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List);
pWorkItem->UserProc(pWorkItem->UserParam);
InterlockedDecrement(&pThreadPool->WorkItemCount);
free(pWorkItem);
}
else
{
_ASSERT(0);
break;
}
}
return 0;
}
BOOL InitializeThreadPool(PTHREAD_POOL pThreadPool, LONG ThreadNum)
{
pThreadPool->QuitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
pThreadPool->WorkItemSemaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
pThreadPool->WorkItemCount = 0;
InitializeListHead(&pThreadPool->WorkItemHeader);
InitializeCriticalSection(&pThreadPool->WorkItemLock);
pThreadPool->ThreadNum = ThreadNum;
pThreadPool->ThreadsArray = (HANDLE*)malloc(sizeof(HANDLE) * ThreadNum);
for(int i=0; i<ThreadNum; i++)
{
pThreadPool->ThreadsArray[i] = CreateThread(NULL, 0, WorkerThread, pThreadPool, 0, NULL);
}
return TRUE;
}
VOID DestroyThreadPool(PTHREAD_POOL pThreadPool)
{
SetEvent(pThreadPool->QuitEvent);
for(int i=0; i<pThreadPool->ThreadNum; i++)
{
WaitForSingleObject(pThreadPool->ThreadsArray[i], INFINITE);
CloseHandle(pThreadPool->ThreadsArray[i]);
}
free(pThreadPool->ThreadsArray);
CloseHandle(pThreadPool->QuitEvent);
CloseHandle(pThreadPool->WorkItemSemaphore);
DeleteCriticalSection(&pThreadPool->WorkItemLock);
while(!IsListEmpty(&pThreadPool->WorkItemHeader))
{
PWORK_ITEM pWorkItem;
PLIST_ENTRY pList;
pList = RemoveHeadList(&pThreadPool->WorkItemHeader);
pWorkItem = CONTAINING_RECORD(pList, WORK_ITEM, List);
free(pWorkItem);
}
}
BOOL PostWorkItem(PTHREAD_POOL pThreadPool, WORK_ITEM_PROC UserProc, PVOID UserParam)
{
PWORK_ITEM pWorkItem = (PWORK_ITEM)malloc(sizeof(WORK_ITEM));
if(pWorkItem == NULL)
return FALSE;
pWorkItem->UserProc = UserProc;
pWorkItem->UserParam = UserParam;
EnterCriticalSection(&pThreadPool->WorkItemLock);
InsertTailList(&pThreadPool->WorkItemHeader, &pWorkItem->List);
LeaveCriticalSection(&pThreadPool->WorkItemLock);
InterlockedIncrement(&pThreadPool->WorkItemCount);
ReleaseSemaphore(pThreadPool->WorkItemSemaphore, 1, NULL);
return TRUE;
}
VOID UserProc1(PVOID dwParam)
{
WorkItem(dwParam);
}
void TestSimpleThreadPool(BOOL bWaitMode, LONG ThreadNum)
{
THREAD_POOL ThreadPool;
InitializeThreadPool(&ThreadPool, ThreadNum);
CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
BeginTime = GetTickCount();
ItemCount = 20;
for(int i=0; i<20; i++)
{
PostWorkItem(&ThreadPool, UserProc1, (PVOID)bWaitMode);
}
WaitForSingleObject(CompleteEvent, INFINITE);
CloseHandle(CompleteEvent);
DestroyThreadPool(&ThreadPool);
}
我们把工作项目放到一个队列中,用一个信号量通知线程池,线程池中任意一个线程取出工作项目来执行,执行完毕之后,线程返回线程池,继续等待新的工作项目。
线程池中线程的数量是固定的,预先创建好的,永久的线程,直到销毁线程池的时候,这些线程才会被销毁。
线程池中线程获得工作项目的机会是均等的,随机的,并没有特别的方式保证哪一个线程具有特殊的优先获得工作项目的机会。
而且,同一时刻可以并发运行的线程数目没有任何限定。事实上,在我们的执行计算任务的演示代码中,所有的线程都并发执行。
下面,我们再来看一下,完成同样的任务,系统提供的线程池是如何运作的。
/**//************************************************************************/
/**//* QueueWorkItem Test. */
/**//************************************************************************/
DWORD BeginTime;
LONG ItemCount;
HANDLE CompleteEvent;
int compute()
{
srand(BeginTime);
for(int i=0; i<20 *1000 * 1000; i++)
rand();
return rand();
}
DWORD WINAPI WorkItem(LPVOID lpParameter)
{
BOOL bWaitMode = (BOOL)lpParameter;
if(bWaitMode)
Sleep(1000);
else
compute();
if(InterlockedDecrement(&ItemCount) == 0)
{
printf("Time total %d second.\n", GetTickCount() - BeginTime);
SetEvent(CompleteEvent);
}
return 0;
}
void TestWorkItem(BOOL bWaitMode, DWORD Flag)
{
CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
BeginTime = GetTickCount();
ItemCount = 20;
for(int i=0; i<20; i++)
{
QueueUserWorkItem(WorkItem, (PVOID)bWaitMode, Flag);
}
WaitForSingleObject(CompleteEvent, INFINITE);
CloseHandle(CompleteEvent);
}
很简单,是吧?我们仅需要关注于我们的回调函数即可。但是与我们的简单模拟来比,系统提供的线程池有着更多的优点。
首先,线程池中线程的数目是动态调整的,其次,线程池利用IO完成端口的特性,它可以限制并发运行的线程数目,默认情况下,将会限制为CPU的数目,这可以减少线程切换。它挑选最近执行过的线程再次投入执行,从而避免了不必要的线程切换。
4.如何在VC中利用系统函数创建一个新的线程
CreateThread函数62616964757a686964616fe59b9ee7ad9431333363393664可以用来创建一个线程,在MSDN中查找这个函数得到如下信息:"The CreateThread function creates a thread to execute within the address space of the calling process."和"If the function succeeds, the return value is a handle to the new thread."所以我们得定义一个句柄用来存放它的返回值。还定义一个指向线程ID的DWORD值dwThreadId。然后我们就可以用CreateThread函数来创建我们的线程了,CreateThread函数有六个参数分别是
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security attributes
DWORD dwStackSize, // initial thread stack size
LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
LPVOID lpParameter, // argument for new thread
DWORD dwCreationFlags, // creation flags
LPDWORD lpThreadId // pointer to receive thread ID
其中第一个参数我们设置为NULL,使这个句柄不能被继承;第二个参数设置为0,使用默认的堆栈大小;第三个参数为线程函数的起始地址,也就是线程函数的函数名;第四个参数为NULL,没有值要传递给线程函数;第五个参数为0,创建好之后马上让线程运行;第六个参数设置为指向线程ID的地址。创建好线程之后,线程函数进行初始化之类的操作,主函数继续执行,此时可以输出被创建线程的ID。我们在主函数中用WaitForSingleObject函数来等待线程函数变成受信(signaled)状态,它的两个参数分别是
HANDLE hHandle, // handle to object to wait for
DWORD dwMilliseconds // time-out interval in milliseconds
第一参数为线程函数的句柄,第二个参数设置为INFINITE,等待线程一直执行完。在程序的最后还要记得用CloseHandle函数关闭线程,这样主函数就写完了。
在线程函数里面我们可以简单地做一些工作,比如设置一个循环,让它输出一定的信息等。源程序如下:
#include <windows.h>
#include <iostream.h>
DWORD WINAPI ThreadFunc(HANDLE Thread)
{
int i;
for(i=0;i<10;i++)
{
cout<<"A new thread has created!"<<endl;
}
return 0;
}
int main(int argc,char* argv[])
{
HANDLE Thread;
DWORD dwThreadId;
Thread=::CreateThread
(NULL,0,ThreadFunc,NULL,0,&dwThreadId);
cout<<"The new thread ID is :"<<dwThreadId<<endl;
::WaitForSingleObject(Thread,INFINITE);
::CloseHandle(Thread);
return 0;
}
在Window xp sp2&VC++ 6.0环境下编译通过。
以上就是关于「vc 如何创建线程」的全部内容,本文讲解到这里啦,希望对大家有所帮助。如果你还想了解更多这方面的信息,记得收藏关注本站~
【Win10系统之家www.ghost580.net①独★家使用,未经允许不得转载!】
相关文章
-
1.VC++如何创建一个线程并传递参数展开全部近来做个项目需要写点程序,比较烦.net写的老是需要背着.net包走,所以准备用VC++来写,其中用到了线程方面的知识这里记录下来以备后用。创建一个工作线程十分简单,只需要两步...
-
1.宽带连接快捷方式怎么创建1、首先打开开始菜单,在开始菜单中打开控制面板,在控制面板中找到网络和internet这个选项,然后单击这个选项,进入网络设置页面。2、在网络和共享中心下面的小链接中,有一个查看网络状态和任...
-
1.微信公众平台怎么创建及操作步骤微信公众号是可以自己申请注册的。选什么类型看自己需要的功能来选择。1.注册微信公众平台需要在微信公众平台官网进行注册。2.需要的材料有:(1)企业/个体工商户开通服务号或者认证号...
-
1.怎么样创建一个FTP地址ftp全称filetransferprotocol,文件传输协议,ftp不仅是一项协议,还是一种服务一种应用,可供用户在不同的设备之间复制文件,用户即可以下载文件又可以上传文件。操作步骤如下:1、点击开始按钮,打...