c#下可以用ManualResetEvent类实现
ManualResetEvent通知一个或多个正在等待的线程已发生事件。 此类不能被继承。
构造函数
public ManualResetEvent(
bool initialState
)
initialState
类型:System.Boolean
如果为 true,则将初始状态设置为终止;如果为 false,则将初始状态设置为非终止。
如果 ManualResetEvent 的初始状态是终止状态(即,如果其通过为 initialState 传递true 创建),则等待 ManualResetEvent 的线程不阻塞。 如果初始状态为非终止状态,则线程调用 Set 方法后才不阻塞。
方法
Set 将事件状态设置为终止状态,允许一个或多个等待线程继续。 (继承自 EventWaitHandle。)
终止状态时WaitOne()允许线程访问下边的语句
非终止状态时WaitOne()阻塞线程,不允许线程访问下边的语句
下面是一个实现Http下载的例子
<span style="font-size:18px;">public class DownloadProcessor
{
public ManualResetEvent allDone = new ManualResetEvent(false);
const int BUFFER_SIZE = 1024;
public byte[] Download(string url, DownloadProgressHandler progressCB)//下载函数
{
// 保证标志设置正确.<span style="white-space:pre"> </span>
allDone.Reset();//执行到WaitOne后,下面的语句会阻塞
//从命令行当中获取url.
Uri httpPath = new Uri(url);
//创建请求对象.
WebRequest req = WebRequest.Create(httpPath);
//创建状态对象.
DownloadDetail detail = new DownloadDetail();
//将请求放入状态对象以便能够被传递.
detail.webRequest = req;
//给回调函数赋值
detail.ProgressCallback += progressCB;
//声明异步请求.
IAsyncResult r = (IAsyncResult)req.BeginGetResponse(new AsyncCallback(ResponseCallback), detail);
//一直阻塞直到ManualResetEvent被置记号,即下载完毕时才继续向下执行
allDone.WaitOne();
//将下载的信息传递回去.
if (detail.useBuffers)
return detail.BufferFast;
else
{
byte[] data = new byte[detail.BufferSlow.Count];
for (int b = 0; b < detail.BufferSlow.Count; b++)
data[b] = (byte)detail.BufferSlow[b];
return data;
}
}
private void ResponseCallback(IAsyncResult iar)
{
//从async中得到DownloadInfo对象
DownloadDetail detail = (DownloadDetail)iar.AsyncState;
//从RequestState中得到WebRequest.
WebRequest webreq = detail.webRequest;
//调用创建WebResponse对象的EndGetResponse
WebResponse webresp = webreq.EndGetResponse(iar);
//从头中找到数据尺寸.
string strLength = webresp.Headers["Content-Length"];
if (strLength != null)
{
detail.Length = Convert.ToInt32(strLength);
detail.BufferFast = new byte[detail.Length];
}
else
{
detail.useBuffers = false;
detail.BufferSlow = new System.Collections.ArrayList(BUFFER_SIZE);
}
//开始从response流中读取数据
Stream ResponseStream = webresp.GetResponseStream();
// 将response stream存储在RequestState当中异步地去读取流
detail.responseStream = ResponseStream;
// 将do.BufferRead传递给BeginRead.
IAsyncResult iarRead = ResponseStream.BeginRead(detail.Read,
0,
BUFFER_SIZE,
new AsyncCallback(ReadCallBack),
detail);
}
private void ReadCallBack(IAsyncResult asyncResult)
{
//从AsyncResult得到DownloadInfo对象.
DownloadDetail detail = (DownloadDetail)asyncResult.AsyncState;
//回收在RespCallback中设置的ResponseStream.
Stream responseStream = detail.responseStream;
// 读info.BufferRead来验证是否包含数据.
int bytesRead = responseStream.EndRead(asyncResult);
if (bytesRead > 0)//数据没有读取完毕,则继续读取
{
if (detail.useBuffers)
{
System.Array.Copy(detail.Read, 0,
detail.BufferFast, detail.bytes,
bytesRead);
}
else
{
for (int b = 0; b < bytesRead; b++)
detail.BufferSlow.Add(detail.Read[b]);
}
detail.bytes += bytesRead;
// 如果进程回调,则通知进度条
if (detail.ProgressCallback != null)
detail.ProgressCallback(detail.bytes, detail.Length);
// 一直读数据直到responseStream.EndRead返回
IAsyncResult ar = responseStream.BeginRead(
detail.Read, 0, BUFFER_SIZE,
new AsyncCallback(ReadCallBack), detail);
}
else
{
responseStream.Close();
allDone.Set();//下载完毕,将事件状态设置为终止状态,WaitOne后面的语句可以执行
}
return;
}
}</span>
win32下c++用CreateEvent
CreateEvent是一个Windows API函数。它用来创建或打开一个命名的或无名的事件对象。
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,// 安全属性
BOOL bManualReset,// 复位方式
BOOL bInitialState,// 初始状态
LPCTSTR lpName // 对象名称
);
lpEventAttributes[输入]
一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。如果lpEventAttributes是NULL,事件将获得一个默认的安全符。
bManualReset[输入]
指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当一个等待线程被释放以后,系统将会自动将事件状态复原为无信号状态。
bInitialState[输入]
指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。
lpName[输入]
指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。如果lpName指定的名字,与一个存在的命名的事件对象的名称相同,函数将请求EVENT_ALL_ACCESS来访问存在的对象。这时候,由于bManualReset和bInitialState参数已经在创建事件的进程中设置,这两个参数将被忽略。如果lpEventAttributes是参数不是NULL,它将确定此句柄是否可以被继承,但是其安全描述符成员将被忽略。
如果lpName为NULL,将创建一个无名的事件对象。如果lpName的和一个存在的信号、互斥、等待计时器、作业或者是文件映射对象名称相同,函数将会失败,在GetLastError函数中将返回ERROR_INVALID_HANDLE。造成这种现象的原因是这些对象共享同一个命名空间。
返回值
如果函数调用成功,函数返回事件对象的句柄。如果对于命名的对象,在函数调用前已经被创建,函数将返回存在的事件对象的句柄,而且在GetLastError函数中返回ERROR_ALREADY_EXISTS。
如果函数失败,函数返回值为NULL,如果需要获得详细的错误信息,需要调用GetLastError。