2019年02月27日 14:41
原创作品,转载时请务必以超链接形式标明文章原始出处,否则将追究法律责任。

本片文章主要介绍如何停止多个有关联的线程,举个例子,抗日战争中,我们要消灭一小队鬼子。但是鬼子装备精良,又有迫击炮,又有机枪。因此要求我们每个小分队必须

责任明确,第一分队负责诱敌深入,第二分队负责第一波阻击,第三分队负责埋雷,第四分队负责冲锋。这其中一环没有做好,都会导致大面积伤亡。比如埋雷的雷没弄好,导致第四队下去冲锋送死。或者第一队没有诱敌深入,那第二队也没法完成阻击。所以要求开始前各分队要有个司令员,如果发现其中一个没有准备好,任务取消。

接下来我们就模拟一下这个例子。

CancellationTokenSource tokenSource;
CancellationToken cancelToken;
List<FightTask> fightTaskList;
int THREAD_COUNT = 10;
const int CHECK_COUNT_PER_THREAD = 50;
private PrecheckResult FightPrepareCheck(PrecheckRequest request)
{
	var result = new PrecheckResult();
	tokenSource = new CancellationTokenSource();
	cancelToken = tokenSource.Token;
	var taskList = new List<Task>();
    fightTaskList = request.FightTaskList;
	if (request.FightTaskList.Count / CHECK_COUNT_PER_THREAD < threadCount)
	{
		if (request.FightTaskList.Count % CHECK_COUNT_PER_THREAD == 0)
		{
			threadCount = request.FightTaskList.Count / CHECK_COUNT_PER_THREAD;
		}
		else
		{
			threadCount = request.FightTaskList.Count / 50 + 1;
		}
	}

	this.position = 0;
	PrecheckResult checkFailedResult = null;
	for (int i = 0; i < threadCount; i++)
	{
		Task task = Task.Factory.StartNew(() =>
		{
			result = this.ProcessTaskCheck();
			if (result != null
				&& result.FightTaskCheckResultList != null
				&& result.FightTaskCheckResultList.Any(b => !b.IsSuccess))
			{
				if (checkFailedResult == null)
				{
					checkFailedResult = result;
				}

				if (!cancelToken.IsCancellationRequested)
				{
					tokenSource.Cancel(true);
				}
			}
		}, cancelToken);
		taskList.Add(task);
	}

	try
	{
		Task.WaitAll(taskList.ToArray(), 300000);
	}
	catch (AggregateException exception)
	{
		exception.Handle(ex =>
		{
			TaskCanceledException tcex = ex as TaskCanceledException;
			if (tcex != null)
			{
				return true;
			}
			else
			{
				result.IsSuccess = false;
				result.ErrorMessage = exception.Message;
				return true;
			}
		});
	}

	if (checkFailedResult != null)
	{
		return checkFailedResult;
	}

	return result;
}

在这里开启线程,每个task都会给他一个取消令牌。然后呢我们去执行ProcessTaskCheck方法,当发现任何一个线程的检查结果中有失败的记录,就将这个结果记录下来,并终止所有还未结束的线程。在这里结束线程前最好判断一下当前线程是否已经请求结束了,比如有两个线程同时到达tokenSource.Cancel(true),就会抛出异常。那么即使抛出异常,我们下边会处理这个异常。判断如果是TaskCancelledException,就默认处理。否则将异常返回给result对象,因为TaskCancelledException不是业务异常。

OK,接下来再看一下ProcessTaskCheck方法

private PrecheckResult ProcessTaskCheck()
{
	var checkResult = new PrecheckResult();
	var fightTasks = new FightTask[] { };
	while (this.position < this.fightTaskList.Count)
	{
		lock (objLock)
		{
			if (position >= fightTaskList.Count)
			{
				break;
			}

			if (position <= fightTaskList.Count - CHECK_COUNT_PER_THREAD)
			{
				fightTasks = new FightTask[CHECK_COUNT_PER_THREAD];
				fightTaskList.CopyTo(position, fightTasks, 0, CHECK_COUNT_PER_THREAD);
			}
			else
			{
				fightTasks = new FightTask[fightTaskList.Count - position];
				fightTaskList.CopyTo(position, fightTasks, 0, fightTasks.Length);
			}

			this.position += this.CHECK_COUNT_PER_THREAD;
		}
		
		return this.DoTaskCheck(fightTasks);
	}
}

很简单,每次拿到一批数据,去check,好的,就到这里。

发表评论
匿名  
用户评论
暂无评论