gpt4 book ai didi

c# - 等待线程完成而不阻塞 UI 线程

转载 作者:行者123 更新时间:2023-11-30 19:54:37 25 4
gpt4 key购买 nike

我正在尝试统一创建一个消息框类,我希望它的工作方式与 Windows 窗体中的消息框相同,后者等待按下按钮,然后执行代码。

        var mbox = MessageBox.Show("Test", "test", MessageBoxButtons.YesNo);
var test = mbox == DialogResult.Cancel; <- it will wait here

我尝试用两种方式重现它

加入2个线程

    public void TestClick()
{
Thread thread1 = new Thread(TestMethod);
thread1.Start();
thread1.Join();
Debug.Log("Done");
}

private void TestMethod()
{
float time = 0;
while (time <= 20)
{
Thread.Sleep(100);
time++;
Debug.Log("Im doing heavy work");
}
}

这会阻塞主线程,只有在 TestMethod 完成后才会恢复,但我不希望这样,因为用户在那段时间无法与消息框进行交互。

异步方法

    public delegate int AsyncTask();

public void TestClick()
{
RunAsyncAndWait();
Debug.Log("Done");
}

public int Method1()
{
float time = 0;
while (time <= 20)
{
Thread.Sleep(100);
time++;
Debug.Log("Im doing heavy work");
}
return 0;
}


public void RunAsyncAndWait()
{
AsyncTask ac1 = Method1;

WaitHandle[] waits = new WaitHandle[1];
IAsyncResult r1 = ac1.BeginInvoke(null, null);
waits[0] = r1.AsyncWaitHandle;

WaitHandle.WaitAll(waits);

}

这与第一个完全一样,但如果我们将 WaitHandle[] 的大小更改为 WaitHandle[] waits = new WaitHandle[2];.

现在它的工作方式更像我需要的,因为它不断地在控制台中写入内容,而不是像以前的方法那样一次发布 21 条消息,但它运行时会暂停统一场景(我可以手动恢复它和程序将运行得很好)并且它继续在控制台中打印内容,我收到此错误

ArgumentNullException: null handle Parameter name: waitHandles System.Threading.WaitHandle.CheckArray (System.Threading.WaitHandle[] handles, Boolean waitAll) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Threading/WaitHandle.cs:77) System.Threading.WaitHandle.WaitAll (System.Threading.WaitHandle[] waitHandles) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Threading/WaitHandle.cs:109) Assets.Scripts.Test.RunAsyncAndWait () (at Assets/Scripts/Test.cs:40) Assets.Scripts.Test.TestClick () (at Assets/Scripts/Test.cs:16) UnityEngine.Events.InvokableCall.Invoke (System.Object[] args) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:153) UnityEngine.Events.InvokableCallList.Invoke (System.Object[] parameters) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:630) UnityEngine.Events.UnityEventBase.Invoke (System.Object[] parameters) (at C:/buildslave/unity/build/Runtime/Export/UnityEvent.cs:765) UnityEngine.Events.UnityEvent.Invoke () (at C:/buildslave/unity/build/Runtime/Export/UnityEvent_0.cs:53) UnityEngine.UI.Button.Press () (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:35) UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/UI/Core/Button.cs:44) UnityEngine.EventSystems.ExecuteEvents.Execute (IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:52) UnityEngine.EventSystems.ExecuteEvents.Execute[IPointerClickHandler] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.EventFunction`1 functor) (at C:/buildslave/unity/build/Extensions/guisystem/UnityEngine.UI/EventSystem/ExecuteEvents.cs:269) UnityEngine.EventSystems.EventSystem:Update()

第一行听起来好像我可能需要一个回调函数,所以我很快添加了一些东西来测试它

IAsyncResult r1 = ac1.BeginInvoke(ar => Debug.Log("Done"), null);

但运气不好,一切都没有改变。

关于我如何从整体上解决这个问题的任何提示(制作消息框,阻塞线程直到按下按钮),或者关于 Microsoft 如何在 Windows 窗体中实现它的更多信息?

最佳答案

WinForms 和 Unity 之间有很大的区别。在 WinForms 中,您有一个用于 UI 的线程,它可能会被模态表单阻塞。在 Unity 中,您有多个对象和多个方法,其中脚本执行顺序和一些引擎机制决定它们在每一帧中的执行方式。

但是,如果您希望在 Unity 中有一个模态消息框,您可以通过向其添加 bool 检查或禁用脚本来简单地阻止特定脚本的 Update 或 FixedUpdate 的执行。第一种方式提供更多选项,但第二种方式更容易。但是请注意,禁用脚本会停止其中的所有内容,InvokeCoroutine 除外。

您可以通过在底层对象上放置一个简单的 SpriteRenderer 或 Image 来阻止用户与底层对象的交互。此 mask 的透明度可以为零,应该是全屏大小并且必须打开Raycast Target

我更喜欢一个带有全屏 mask 的消息框,它有一个带有 alpha = .1 的简​​单黑色 Sprite

public GameObject ModalMessageBox;//game object of message box with a mask

public void TestClick()
{
StartCoroutine(TestMethod);
ModalMessageBox.setActive(true);
}

IEnumerator TestMethod()
{
float time = 0;
while (time <= 20)
{
yield return new WaitForSeconds(.1f);
time++;
Debug.Log("Im doing heavy work");
}
ModalMessageBox.setActive(false);
}

void Update()
{
if(ModalMessageBox.activeSelf)
{
//handle message box
}
else
{
//handle normal update stuff
}
}

请注意,所有其他脚本仍然会运行。如果您还必须阻止其他脚本的执行,那么您需要一个一个地执行。

注意:

由于禁用脚本不会停止它启动的协程,因此您不妨禁用脚本本身

public Script1 script1;
public Script2 script2;
public Script3 script3;

void BlockScripts(bool block)
{
//for singleton scripts:
Script1.Instance.enabled = !block;
Script2.Instance.enabled = !block;
Script3.Instance.enabled = !block;
//for referenced scripts:
script1.enabled = !block;
script2.enabled = !block;
script3.enabled = !block;

//self
enabled = !block;
}

public void TestClick()
{
StartCoroutine(TestMethod);
ModalMessageBox.setActive(true);

BlockScripts(true);
}

IEnumerator TestMethod()
{
float time = 0;
while (time <= 20)
{
yield return new WaitForSeconds(.1f);
time++;
Debug.Log("Im doing heavy work");
}

ModalMessageBox.setActive(false);

BlockScripts(false);
}

void Update()
{
}

其中 Script1、2、3 是单例类,script1、2、3 是您要阻止的脚本的引用。

关于c# - 等待线程完成而不阻塞 UI 线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41202166/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com