在项目制作的过程中,由于程序需要处理的数据量比较大,所以采用异步的方式来进行,也就是利用委托的BeginInvoke方法和EndInvoke方法来进行。效果很不错,达到了预期。但是由于程序在运行的时候,需要先加载数据,数据加载完成以后,然后进行数据比对差异,并对有差异的数据进行着色处理,在这儿,问题就来了。

  什么问题呢?我们知道,当利用异步的时候,实际上系统会自动为我们创建前后台交互线程,那么这个数据加载和差异着色就运行在了这样的线程之上,所以二者的运行先后顺序是不可知的,事实上,程序中着色的混乱也验证了这一点:那就是我虽然原本想让数据加载完成后在进行代码差异着色,可是实际运行的时候发现,数据是边加载边着色的,这也归咎到如何将多个线程合并为一个并且按序运行的问题。

  数据加载的异步代码如下:

 //start to perform async action
private void BeginDealDataInformation(ListView listview,string fileName,EnumX enums)
{
DealDataInformationDelegateAsync dAsync = new DealDataInformationDelegateAsync(DealDataInformation);
IAsyncResult iar = dAsync.BeginInvoke(listview,fileName,enums,new AsyncCallback(EndDealDataInformation),dAsync);

if ((enumThis = enums) == EnumX.leftSide) tsStatus.Text = "正在加载左边文件的数据,请等待...";
if ((enumThis = enums) == EnumX.rightSide) tsStatus.Text = "正在加载右边文件的数据,请等待...";
}

//returen the async running result
private void EndDealDataInformation(IAsyncResult iar)
{
UIThread.UIInvoke(this.toolStrip1, delegate
{
if (enumThis == EnumX.leftSide) tsStatus.Text = "左边数据加载完毕,请继续下面操作...";
if (enumThis == EnumX.rightSide) tsStatus.Text = "右边数据加载完毕,请继续下面操作...";
});
DealDataInformationDelegateAsync dAsync = (DealDataInformationDelegateAsync)iar.AsyncState;
dAsync.EndInvoke(iar);
}


//this is a function that takes a lot of time to run, need to use async action to perform it.
private void DealDataInformation(ListView listview,string fileName,EnumX enums)
{
//myHandle.Reset();//让其他需要等待的线程阻塞
if (ColumnHeaderCollection == null) throw new Exception("文件内容为空或者不以逗号分隔!!!");

int columnCount = ColumnHeaderCollection.Length;
for (int i = 0; i < columnCount; i++)
{
ColumnHeader header = new ColumnHeader();
header.Text = ColumnHeaderCollection[i];
header.TextAlign = HorizontalAlignment.Left;

UIThread.UIInvoke(listview, delegate {
listview.Columns.Add(header);
});
}

int tryResult;

using (StreamReader sr = new StreamReader(fileName, Encoding.Default))
{
string result;
while ((result = sr.ReadLine()) != null)
{
string[] _result = result.Split(',');
ListViewItem lvi = new ListViewItem(_result[0]);

if (Int32.TryParse(_result[0], out tryResult))
{
if (enums == EnumX.leftSide) DataCache.cacheRecordListOne.Add(Int32.Parse(_result[0])); //cache the record
if (enums == EnumX.rightSide) DataCache.cacheRecordListTwo.Add(Int32.Parse(_result[0])); //cache the record
}

for (int j = 1; j < _result.Length; j++)
{
lvi.SubItems.Add(_result[j]);
}

//add items
UIThread.UIInvoke(listview, delegate
{
listview.Items.Add(lvi);
if (enums == EnumX.leftSide) DataCache.listItemLeft.Add(lvi);
if (enums == EnumX.rightSide) DataCache.listItemRight.Add(lvi);
});
}
}

//remove the first item(commonly, it's a header)
UIThread.UIInvoke(listview, delegate
{
listview.Items.RemoveAt(0);
//DataCache.listItemLeft.RemoveAt(0);
//DataCache.listItemRight.RemoveAt(0);
});
//myHandle.Set(); //允许其他等待的线程运行
}

 这里我们可以看到需要传入3个参数。

差异着色的异步处理方法如下:

 private void ColorTheResultAsync(EnumX enums,ListView listview)
{
//myHandle.WaitOne(); //处于等待状态,直到收到Set的信号
CommonUntil commonUntil = new CommonUntil();
List<int> existList=null;
List<int> nonExistList = null;

if (enums == EnumX.leftSide)
{
existList = commonUntil.ReturnComparisonResult(DataCache.cacheRecordListOne, DataCache.cacheRecordListTwo)[0];
nonExistList=commonUntil.ReturnComparisonResult(DataCache.cacheRecordListOne, DataCache.cacheRecordListTwo)[1];

listPartOne[0] = existList;
listPartOne[1] = nonExistList;
}

if (enums == EnumX.rightSide)
{

existList = commonUntil.ReturnComparisonResult(DataCache.cacheRecordListTwo, DataCache.cacheRecordListOne)[0];
nonExistList = commonUntil.ReturnComparisonResult(DataCache.cacheRecordListTwo, DataCache.cacheRecordListOne)[1];

listPartTwo[0] = existList;
listPartTwo[1] = nonExistList;
}

int __result;
UIThread.UIInvoke(listview, delegate
{

foreach (ListViewItem lvi in listview.Items)
{

if (Int32.TryParse(lvi.Text, out __result))
{
if (existList.Contains(Int32.Parse(lvi.Text)))
{
if (enums == EnumX.leftSide) lvi.BackColor = Color.Wheat;
if (enums == EnumX.rightSide) lvi.BackColor = Color.YellowGreen;
}
}
}
listview.Invalidate();
});
}

private void BeginColorTheResultAsync(EnumX enums,ListView listview)
{
ColorTheResultDelegateAsync colorTheResultAsync = new ColorTheResultDelegateAsync(ColorTheResultAsync);
IAsyncResult iar = colorTheResultAsync.BeginInvoke(enums, listview, new AsyncCallback(EndColorTheResultAsync),colorTheResultAsync);

tsStatus.Text = "正在着色中,请等待...";
}

private void EndColorTheResultAsync(IAsyncResult iar)
{
tsStatus.Text = "着色完毕,请检测...";
ColorTheResultDelegateAsync colorTheResultAsync = (ColorTheResultDelegateAsync)iar.AsyncState;
colorTheResultAsync.EndInvoke(iar);
}

  可以看到需要两个参数来运行。

  本来想直接开两个线程,然后利用Thread的join方法将线程合并为一个,然后顺序运行,以达到需要的效果,但是需要传至少两个参数进去,查看了一下ParameterizedThreadStart,发现结尾只能带一个object类型的参数,但是由于我这里传入的多于一个(虽然可以将这些参数用类包装起来,当作object传过去,但是那样确实比较麻烦,我还不确定这个程序需要不需要扩展呢),于是只要寻找别的方法。

  在查资料的过程中,我突然想到一个类:EventWaitHandle,也就是本文的主角。

  这个类通过在线程之间设置信号量,可以非常方便的控制线程运行的顺序。具体代码如下:

  首先全局申明:

 EventWaitHandle myHandle = new EventWaitHandle(false, EventResetMode.ManualReset);  //将信号状态置为非终止,使用手动重置

  其次在大数据处理的函数开始加上

 myHandle.Reset();//让其他需要等待的线程阻塞

  末尾加上:

myHandle.Set();  //允许其他等待的线程运行

  具体形式如下:

 private void DealDataInformation(ListView listview,string fileName,EnumX enums)
{
myHandle.Reset();//让其他需要等待的线程阻塞
    ....................
myHandle.Set(); //允许其他等待的线程运行
}

  其中Reset方法可以让本函数进行处理,而让其他在线程上的未接收到信号量的函数进入阻塞状态,而Set方法则是释放信号量,以便通知阻塞线程当前处理结束,可以继续进行。

  那么怎么控制哪些函数需要阻塞呢? 很简单:

  直接在函数的入口处加上:

private void ColorTheResultAsync(EnumX enums,ListView listview)
{
myHandle.WaitOne(); //处于等待状态,直到收到Set的信号
.........
}

就表明只有当承载DealDataInformation函数的线程运行完毕,承载ColorTheResultAsync函数的线程才可以开始运行,这样就做到了顺序运行。

posted on 2011-12-21 20:26  程序诗人  阅读(4868)  评论(0编辑  收藏  举报