【发布时间】:2016-11-02 11:57:40
【问题描述】:
我有一段代码可以进行一些计算,然后调用 form.show 命令。现在我有一个库(revit api),它不允许我在项目中存储变量而不是在主线程中。
对此的逻辑解决方案是让生成的线程使用生产者/消费者模式调用主线程,代码看起来有点像这样:
form.Show(owner);
while(AppIsRunning){
if(clicked)
commit();
else
Thread.sleep(100);
}
但是,当我这样做时,gui 不会完全加载(黑色背景,按钮分机中没有文本)。
我也尝试过使用 evoke 方法来做到这一点
private void BtnOK_Click(object sender, System.EventArgs e)
{
Commit();
Invoke(Commit);
}
private void Invoke(Action commit)
{
commit.Invoke();
}
但这只是告诉我,执行提交函数的不是主线程。 有没有其他方法可以做到这一点,或者我只是犯了一个错误。
为了清楚起见,我有一个 form.show(owner) 命令,如果它不是由主线程执行,则会引发错误。我还有一个 commit() 函数,必须由主线程原谅,否则它会引发错误。执行必须等到按下按钮。但是主线程轮询gui线程以进行更改会导致程序挂起。根据我的谷歌搜索,也可以做一些涉及外部事件的事情以返回正确的上下文,但给出的示例是使用 python 调用 c# 代码,有没有一种好方法来引发外部事件以返回c# 中的给定线程?
编辑:根据一些建议,我创建了以下代码:
public class ThreadManager
{
static List<ThreadAble> orders = new List<ThreadAble>();
public static bool running = false;
public static void execute(ThreadAble action)
{
orders.Add(action);
}
static System.Timers.Timer timer;
public static void RegisterAPIThreadAndHold(ExternalCommandData commandData)
{
UIApplication uiapp = commandData.Application;
uiapp.Idling += Application_Idle;
}
private static void Application_Idle(Object o,IdlingEventArgs e)
{
if (orders.Count != 0)
{
ThreadAble f = orders.First();
orders.Remove(f);
f.execute();
}
}
}
public interface ThreadAble {
void execute();
}
但是,当我将它用作 public override Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
Form frm = new OverviewForm(ExternalCommandData commandData);
frm.show()
ThreadManager.RegisterAPIThreadAndHold(commandData);
ThreadManager.Execute(new run_ThrowError())
ThrowError.execute() 在哪里 throw new Exception("这实际上正在执行");
【问题讨论】:
-
我希望这足够清楚,如果您有任何问题,请随时发表评论。
-
你应该在另一个线程上工作,而不是 UI 线程。
-
@DanielA.White 我有一个 button.OnClick 事件,我可以将线程附加到该事件,但这似乎使用了 gui 线程。如何将其移至主线程?
-
您应该创建自己的线程或使用线程池中的一个。
-
@DanielA.White 到底怎么样?我的意思是我的主线程必须是调用 GUI 线程的那个(否则它会引发异常),然后从 GUI 线程我希望主线程再次执行一些代码。
标签: c# multithreading user-interface producer-consumer revit-api