问题在这里http://bbs.csdn.net/topics/390369970
既要阻塞UI线程等待非UI线程结束,又要在非UI线程中进行UI操作,简单的Wait肯定会造成死锁。
因为很多时候UI与非UI操作夹杂在一起,写程序很方便。
为了解决这个问题,所以我写了一个比较通用的基于fastCSharp处理类。
就是建立一个UI任务队列,UI线程Wait新任务,非UI线程Pulse唤醒UI线程。
using System;
using System.Threading;
using fastCSharp;
using fastCSharp.threading;namespace showjim.console
{
/// <summary>
/// UI多任务阻塞处理
/// </summary>
public class uiWait
{
/// <summary>
/// 任务信息
/// </summary>
private class taskInfo
{
/// <summary>
/// 执行委托
/// </summary>
public action Action;
/// <summary>
/// 等待完成锁
/// </summary>
public object WaitLock;
/// <summary>
/// 任务是否已完成
/// </summary>
public bool isFinally;
/// <summary>
/// 等待任务完成
/// </summary>
public void Wait()
{
Monitor.Enter(WaitLock);
try
{
if (!isFinally) Monitor.Wait(WaitLock);
}
finally { Monitor.Exit(WaitLock); }
}
/// <summary>
/// 执行任务
/// </summary>
public void Run()
{
if (Action != null)
{
try
{
Action();
}
catch (Exception error)
{
fastCSharp.log.Default.Add(error, null, false);
}
}
if (WaitLock != null)
{
isFinally = true;
Monitor.Enter(WaitLock);
try
{
Monitor.Pulse(WaitLock);
}
finally { Monitor.Exit(WaitLock); }
}
}
}
/// <summary>
/// 非UI任务
/// </summary>
private readonly task task;
/// <summary>
/// UI任务集合
/// </summary>
private list<taskInfo> tasks = new list<taskInfo>();
/// <summary>
/// UI任务访问锁
/// </summary>
private readonly object taskLock = new object();
/// <summary>
/// 是否开始运行UI任务
/// </summary>
private bool isRun;
/// <summary>
/// 是否以线程的方式运行UI任务
/// </summary>
private bool isRunThread;
/// <summary>
/// 非UI任务是否已完成
/// </summary>
private bool isTaskFinally;
/// <summary>
/// UI任务是否已完成
/// </summary>
private bool isFinally;
/// <summary>
/// UI任务完成锁
/// </summary>
private readonly object finallyLock = new object();
/// <summary>
/// UI多任务阻塞处理
/// </summary>
/// <param name="task">非UI任务</param>
public uiWait(task task)
{
if (task == null) fastCSharp.log.Default.Throw(log.exceptionType.Null);
this.task = task;
}
/// <summary>
/// 添加新的UI任务
/// </summary>
/// <param name="task">任务信息</param>
private void add(taskInfo task)
{
Monitor.Enter(taskLock);
try
{
tasks.Add(task);
Monitor.Pulse(taskLock);
}
finally { Monitor.Exit(taskLock); }
}
/// <summary>
/// 添加新的UI任务
/// </summary>
/// <param name="run">任务执行委托</param>
public void Add(action run)
{
if (run != null) add(new taskInfo { Action = run });
}
/// <summary>
/// 添加新的UI任务
/// </summary>
/// <typeparam name="parameterType">任务参数类型</typeparam>
/// <param name="run">任务执行委托</param>
/// <param name="parameter">任务参数</param>
public void Add<parameterType>(action<parameterType> run, parameterType parameter)
{
if (run != null) add(new taskInfo { Action = run<parameterType>.Create(run, parameter) });
}
/// <summary>
/// 添加新的UI任务并同步等待任务完成
/// </summary>
/// <param name="info">任务信息</param>
private void addWait(taskInfo info)
{
add(info);
info.Wait();
}
/// <summary>
/// 添加新的UI任务并同步等待任务完成
/// </summary>
/// <param name="run">任务执行委托</param>
public void AddWait(action run)
{
addWait(new taskInfo { Action = run, WaitLock = new object() });
}
/// <summary>
/// 添加新的UI任务并同步等待任务完成
/// </summary>
/// <typeparam name="parameterType">任务参数类型</typeparam>
/// <param name="run">任务执行委托</param>
/// <param name="parameter">任务参数</param>
public void AddWait<parameterType>(action<parameterType> run, parameterType parameter)
{
addWait(new taskInfo { Action = run<parameterType>.Create(run, parameter), WaitLock = new object() });
}
/// <summary>
/// 检测是否开始运行UI任务
/// </summary>
private void checkRun()
{
Monitor.Enter(taskLock);
try
{
if (isRun) fastCSharp.log.Default.ThrowReal(log.exceptionType.ErrorOperation);
isRun = true;
}
finally { Monitor.Exit(taskLock); }
}
/// <summary>
/// 等待非UI任务结束
/// </summary>
private void wait()
{
task.Dispose(true);
isTaskFinally = true;
Monitor.Enter(taskLock);
try
{
Monitor.Pulse(taskLock);
}
finally { Monitor.Exit(taskLock); }
}
/// <summary>
/// 执行UI任务
/// </summary>
private void run()
{
list<taskInfo> runTasks = new list<taskInfo>(), oldTasks;
while (true)
{
Monitor.Enter(taskLock);
try
{
if (tasks.Count == 0)
{
if (isTaskFinally) break;
Monitor.Wait(taskLock);
}
oldTasks = runTasks;
runTasks = tasks;
tasks = oldTasks;
}
finally { Monitor.Exit(taskLock); }
foreach (taskInfo task in runTasks) task.Run();
runTasks.Empty();
}
isFinally = true;
Monitor.Enter(finallyLock);
try
{
Monitor.Pulse(finallyLock);
}
finally { Monitor.Exit(finallyLock); }
}
/// <summary>
/// 异步执行UI任务
/// </summary>
public void RunThread()
{
checkRun();
new thread(wait).Start();
new thread(run).Start();
isRunThread = true;
}
/// <summary>
/// 等待异步执行UI任务结束
/// </summary>
public void WaitThread()
{
if (isRunThread)
{
Monitor.Enter(finallyLock);
try
{
if (!isFinally) Monitor.Wait(finallyLock);
}
finally { Monitor.Exit(finallyLock); }
}
else fastCSharp.log.Default.ThrowReal(log.exceptionType.ErrorOperation);
}
/// <summary>
/// 执行UI任务并等待任务结束
/// </summary>
public void Wait()
{
checkRun();
new thread(wait).Start();
run();
}
}
}
支持同步与异步两种模式,uiWait是一个实例
uiWait.Wait();
uiWait.RunThread();
//do something
uiWait.WaitThread();UI线程阻塞多任务
既要阻塞UI线程等待非UI线程结束,又要在非UI线程中进行UI操作,简单的Wait肯定会造成死锁。
因为很多时候UI与非UI操作夹杂在一起,写程序很方便。
为了解决这个问题,所以我写了一个比较通用的基于fastCSharp处理类。
就是建立一个UI任务队列,UI线程Wait新任务,非UI线程Pulse唤醒UI线程。
using System;
using System.Threading;
using fastCSharp;
using fastCSharp.threading;namespace showjim.console
{
/// <summary>
/// UI多任务阻塞处理
/// </summary>
public class uiWait
{
/// <summary>
/// 任务信息
/// </summary>
private class taskInfo
{
/// <summary>
/// 执行委托
/// </summary>
public action Action;
/// <summary>
/// 等待完成锁
/// </summary>
public object WaitLock;
/// <summary>
/// 任务是否已完成
/// </summary>
public bool isFinally;
/// <summary>
/// 等待任务完成
/// </summary>
public void Wait()
{
Monitor.Enter(WaitLock);
try
{
if (!isFinally) Monitor.Wait(WaitLock);
}
finally { Monitor.Exit(WaitLock); }
}
/// <summary>
/// 执行任务
/// </summary>
public void Run()
{
if (Action != null)
{
try
{
Action();
}
catch (Exception error)
{
fastCSharp.log.Default.Add(error, null, false);
}
}
if (WaitLock != null)
{
isFinally = true;
Monitor.Enter(WaitLock);
try
{
Monitor.Pulse(WaitLock);
}
finally { Monitor.Exit(WaitLock); }
}
}
}
/// <summary>
/// 非UI任务
/// </summary>
private readonly task task;
/// <summary>
/// UI任务集合
/// </summary>
private list<taskInfo> tasks = new list<taskInfo>();
/// <summary>
/// UI任务访问锁
/// </summary>
private readonly object taskLock = new object();
/// <summary>
/// 是否开始运行UI任务
/// </summary>
private bool isRun;
/// <summary>
/// 是否以线程的方式运行UI任务
/// </summary>
private bool isRunThread;
/// <summary>
/// 非UI任务是否已完成
/// </summary>
private bool isTaskFinally;
/// <summary>
/// UI任务是否已完成
/// </summary>
private bool isFinally;
/// <summary>
/// UI任务完成锁
/// </summary>
private readonly object finallyLock = new object();
/// <summary>
/// UI多任务阻塞处理
/// </summary>
/// <param name="task">非UI任务</param>
public uiWait(task task)
{
if (task == null) fastCSharp.log.Default.Throw(log.exceptionType.Null);
this.task = task;
}
/// <summary>
/// 添加新的UI任务
/// </summary>
/// <param name="task">任务信息</param>
private void add(taskInfo task)
{
Monitor.Enter(taskLock);
try
{
tasks.Add(task);
Monitor.Pulse(taskLock);
}
finally { Monitor.Exit(taskLock); }
}
/// <summary>
/// 添加新的UI任务
/// </summary>
/// <param name="run">任务执行委托</param>
public void Add(action run)
{
if (run != null) add(new taskInfo { Action = run });
}
/// <summary>
/// 添加新的UI任务
/// </summary>
/// <typeparam name="parameterType">任务参数类型</typeparam>
/// <param name="run">任务执行委托</param>
/// <param name="parameter">任务参数</param>
public void Add<parameterType>(action<parameterType> run, parameterType parameter)
{
if (run != null) add(new taskInfo { Action = run<parameterType>.Create(run, parameter) });
}
/// <summary>
/// 添加新的UI任务并同步等待任务完成
/// </summary>
/// <param name="info">任务信息</param>
private void addWait(taskInfo info)
{
add(info);
info.Wait();
}
/// <summary>
/// 添加新的UI任务并同步等待任务完成
/// </summary>
/// <param name="run">任务执行委托</param>
public void AddWait(action run)
{
addWait(new taskInfo { Action = run, WaitLock = new object() });
}
/// <summary>
/// 添加新的UI任务并同步等待任务完成
/// </summary>
/// <typeparam name="parameterType">任务参数类型</typeparam>
/// <param name="run">任务执行委托</param>
/// <param name="parameter">任务参数</param>
public void AddWait<parameterType>(action<parameterType> run, parameterType parameter)
{
addWait(new taskInfo { Action = run<parameterType>.Create(run, parameter), WaitLock = new object() });
}
/// <summary>
/// 检测是否开始运行UI任务
/// </summary>
private void checkRun()
{
Monitor.Enter(taskLock);
try
{
if (isRun) fastCSharp.log.Default.ThrowReal(log.exceptionType.ErrorOperation);
isRun = true;
}
finally { Monitor.Exit(taskLock); }
}
/// <summary>
/// 等待非UI任务结束
/// </summary>
private void wait()
{
task.Dispose(true);
isTaskFinally = true;
Monitor.Enter(taskLock);
try
{
Monitor.Pulse(taskLock);
}
finally { Monitor.Exit(taskLock); }
}
/// <summary>
/// 执行UI任务
/// </summary>
private void run()
{
list<taskInfo> runTasks = new list<taskInfo>(), oldTasks;
while (true)
{
Monitor.Enter(taskLock);
try
{
if (tasks.Count == 0)
{
if (isTaskFinally) break;
Monitor.Wait(taskLock);
}
oldTasks = runTasks;
runTasks = tasks;
tasks = oldTasks;
}
finally { Monitor.Exit(taskLock); }
foreach (taskInfo task in runTasks) task.Run();
runTasks.Empty();
}
isFinally = true;
Monitor.Enter(finallyLock);
try
{
Monitor.Pulse(finallyLock);
}
finally { Monitor.Exit(finallyLock); }
}
/// <summary>
/// 异步执行UI任务
/// </summary>
public void RunThread()
{
checkRun();
new thread(wait).Start();
new thread(run).Start();
isRunThread = true;
}
/// <summary>
/// 等待异步执行UI任务结束
/// </summary>
public void WaitThread()
{
if (isRunThread)
{
Monitor.Enter(finallyLock);
try
{
if (!isFinally) Monitor.Wait(finallyLock);
}
finally { Monitor.Exit(finallyLock); }
}
else fastCSharp.log.Default.ThrowReal(log.exceptionType.ErrorOperation);
}
/// <summary>
/// 执行UI任务并等待任务结束
/// </summary>
public void Wait()
{
checkRun();
new thread(wait).Start();
run();
}
}
}
支持同步与异步两种模式,uiWait是一个实例
uiWait.Wait();
uiWait.RunThread();
//do something
uiWait.WaitThread();UI线程阻塞多任务
if (run != null) add(new taskInfo { Action = run<parameterType>.Create(run, parameter) });报错
VS2012 没有引用fastCSHarp ,我去找个试下
之所以不让UI线程阻塞,是要让程序注重起码的用户体验。而你杜撰出来“肯定造成死锁”,先恐吓一下,就会让那些不懂技术的人盲目跟风,更不懂如何阻塞了。你看似抛出了一个榴莲,其实目的是不让人家吃水果了,只能吃你的榴莲。
既然这样,那就麻烦大神你也抛一个水果出来,让大家学习学习啊,谢谢。
这个需求可不是我自己假设出来的,应该是http://bbs.csdn.net/topics/390369970楼主的真实需求吧。
当然也可能是我对楼主的需求理解错误,那么也有请大神抛一个水果出来,解决一下实际问题。
如果是一个子线程,确实不如直接嵌入主线程执行,这个建议我已经在楼主帖子的回帖中说过了。
我以为楼主的需求是多个子线程需要并行,至于阻塞主线程也是楼主的需求。我在楼主的回帖中说过,这种程序流程的设计是不正常的,我只是想尽可能少的改动源代码,以解决这个问题。
fastCSharp是支持.NET2.0的,开发效率与运行效率都优于.NET原生代码。
至于稳定性,我是应用于自己所做的实际项目的,应该也不会太差。
要编译.net3.5及以上版本,需要在 项目属性->生成 设置条件编译符号 DOTNET35。
其实我只是要试验下你的代码能实现的效果,我也看了下微软的async/await关键字的处理,最终确定要实现完美的界面无阻塞,不是那么简单的,微软背后进行了偷梁换柱才实现了这样的效果,而你的代码就那么点内容,绝对不可能实现那种效果。
uiWait.Add(()=>{...});
包装起来是可以了,不需要Invoke,因为最后是在主线程中执行的任务。如果是封装数量多导致UI卡住(不是死锁),那么Invoke只会更慢。
能不能实现,测试过了就知道了。
如果没有阻塞UI线程的需求,根本就没必要用到这个uiWait。
首先是一段正常执行的代码,因为耗时非常少,所以放在UI线程中直接执行,然后一段非常耗时的代码进来了,要不卡界面,只能用多线程执行,但是我需要在这个耗时代码结束的时候,继续执行UI操作,虽然可以通过委托回调的方式,在多线程结束的时候回调去处理,但是这样代码看起来就被分隔开了,不易读懂,编码量也增加了。微软在NET4.5里面是这样处理的,非常耗时的多线程操作作为UI线程的不部分,通过await关键字去等待该部分的执行结束,而这个等待可以做到不卡界面,只要执行的过程是异步的(同步的过程仍旧会卡界面的),如果不用await去等待,异步执行后面的代码会立刻执行,用了await去等待,后面的代码会等待这个异步过程结束后才去执行,并且等待中不卡界面。
使用async/await关键字后,整个多线程编程过程中,随意插进来的多线程部分等待和同步等待一样进行编码,代码看起来非常舒服。(编译后的IL偷梁换柱,很难读懂的)
要写个通用的也容易,那就没法使用IO线程干活了,只能自己开线程或者添加到某个任务中执行。现在我还没碰到过这种需求,所以就没有写这部分,具体实现可以参考Stream。
{
asynchronous.streamEndReader outputReader = srOut.BaseStream.readToEndAsync();
asynchronous.streamEndReader errorReader = srError.BaseStream.readToEndAsync();
process.WaitForExit(10000); if (!process.HasExited)
{
KillProcess(process);
item.State = dataModel.judge.judgeState.CompileError;
item.Message = "编译超时";
//item.Message += outputReader.Wait().ToString(Encoding.Default);
//item.Message += errorReader.Wait().ToString(Encoding.Default);
}
else
{
item.Message += outputReader.Wait().ToString(Encoding.Default);
item.Message += errorReader.Wait().ToString(Encoding.Default); if (process.ExitCode != 0)
item.State = dataModel.judge.judgeState.CompileError;
else
item.State = dataModel.judge.judgeState.Compiled;
}
里面的用法 asynchronous.streamEndReader outputReader = srOut.BaseStream.readToEndAsync();
asynchronous.streamEndReader errorReader = srError.BaseStream.readToEndAsync();
//...
item.Message += outputReader.Wait().ToString(Encoding.Default);
item.Message += errorReader.Wait().ToString(Encoding.Default);
你这样给代码比较难理解,最好有个使用部分的完整Demo,显然你只是在说明你的核心代码如何在工作,但我现在对你的代码如果调用还不清楚,自然要演示一个效果出来也难。我需要先看到它能做到什么,通过实际的代码来说明,然后才去看它如何实现的。
就是产生一个异步任务outputReader.Wait()
就是等待任务结束,相当于await
一个窗口,里面一个按钮,按钮添加一个点击事件。
事件的开头先new另一个窗口出来,那个窗口里面只有一个进度条,开启自己滚动的效果。
在show这个窗口出来后,执行另一个耗时的方法,这个方法就一行代码Thread.Sleep(10000);
这个耗时方法结束后,关闭打开的窗口。我需要的效果是,在这个10秒内,界面不会卡,不会卡的表现就是那个新建的窗口中的滚动条能够正常滚动。同时这个窗口必须等待10秒后关闭,而所有这些方法要写在按钮的点击事件中,只有那个耗时的方法(10秒等待)是封装的,为了开启多线程。
uiWait = new uiWait(task);
进度条 进度条 = new 进度条();
task.Add(() =>
{
Thread.Sleep(10000);
uiWait.Add(() => 进度条.关闭());
});
task.Add(() =>
{
while (uiWait.AddWait(() =>
{
if (!进度条.是否关闭())
{
进度条.进度 = 获取当前进度();
return true;
}
return false;
}))
{
Thread.Sleep(1);
}
});
uiWait.Wait();
不好意思,只能给个伪代码,我对winform不熟悉,只会拖个按钮什么的。
但是这个功能想要模拟,难度非常大,而.NET4.5不支持XP,因此又不敢去用。
你能不能提供这个简单的源代码,我试试使用uiWait改写。
可以考虑自己解析LambdaExpression,对其中的UI操作改为Invoke,这个工作量不小。
进度条窗口可以在UI线程卡死的情况下运行吗?还是说,那是一个新的窗口进程?以前不知道SynchronizationContext这个东西,看了一下介绍,像是一个.NET平台通用的“Invoke”。谢谢!
那个是新建的STA线程,和后台线程有点不同,能够运行UI控件,你理解为一个程序同时开了2个不相干的窗口即可,进程显示的还是一个,但2个窗口互相之间不会干扰。虽然如此,但我可以通过设置静态变量来访问共享对象,进而干预另一个窗口。而这里也可以使用SynchronizationContext将这个线程的任务发送到另一个线程去执行,从而用户能够随时看到处理进度,但是界面却一直能响应。
而且我对于winform也没什么兴趣,如果将来有时间倒是可以学一学wpf,但是我不想把时间花在界面布局上面。所以我的fastCSharp涉及的都是与界面无关的东西。
除非函数功能具有严格层次关系,如果post次数过多的话代码写出来也很难看,比如 //do something
context.post(() =>
{
//do something
context.post(() =>
{
//do something
context.post(() =>
{
//do something
}, null);
}, null);
}, null);
这个小写的post是经过包装的。
你的这个代码其实写的不好,SynchronizationContext不应该迭代。
规范的代码编写方式是这样的:
先不管是否需要异步操作,按照同步操作编写代码。全部写好后,对其中的UI操作部分进行单独处理,添加
context.Post(t =>
{},this);
如果是获取变量内容,则必须用Send请求。这里的模型甚至可以用代码生成器自动产生,前提是要添加#regin标签进行代码分类,便于识别。它的好处是可读写强。如果这里不传递this对象,为空也可以,但是执行效率会降低些,因为如果不是通过变量传递,直接访问外部变量的话,这个匿名委托的处理方式就会有不同,编译器会创建一个匿名类来传递外部变量的。