借助人工智能进行工作和学习,变得越来越重要。这是一个需要不断摸索,积累,练习的成长过程。
我通过一系列的帖子来记录详细的过程,可以发现:
- 如何通过与人工智能的交互激发自己的思考
- 通过人工智能给出的信息调动自己动手实践
- 从一个切入点展开各种靠单纯学习知识或课程关注不到的实战问题
- 思考问题,自主学习能力的提升
- 更自由更接近知识与应用本质的学习过程
⛳切入点,用C#来实现Coroutine方法
以下粗标题都是向chat提问的问法
用C#实现Coroutines & Yield
在C#中,协程与yield关键字结合使用可以实现可暂停和恢复执行的功能。下面是一个简单的示例:
using System;
using System.Collections;
class CoroutineExample
{
static void Main(string[] args)
{
IEnumerator coroutine = Coroutine();
while (coroutine.MoveNext())
{
// Do something
}
Console.WriteLine("Coroutine finished");
}
static IEnumerator Coroutine()
{
Console.WriteLine("Coroutine started");
yield return null; // Yield to main loop
Console.WriteLine("Coroutine resumed");
yield return new WaitForSeconds(2); // Yield for 2 seconds
Console.WriteLine("Coroutine resumed");
yield return new WaitForSeconds(3); // Yield for 3 seconds
Console.WriteLine("Coroutine finished");
}
}
class WaitForSeconds
{
private readonly float duration;
private DateTime endTime;
public WaitForSeconds(float seconds)
{
duration = seconds;
endTime = DateTime.Now.AddSeconds(duration);
}
public bool IsFinished()
{
return DateTime.Now >= endTime;
}
}
上述代码中,CoroutineExample类中的Main方法启动了一个协程,并在循环中不断调用MoveNext()方法以推动协程的执行。Coroutine方法是一个迭代器方法,用于定义协程的逻辑。在Coroutine方法中,可以使用yield关键字暂停和恢复协程的执行。在示例中,首先打印"Coroutine started",然后通过yield return null将控制权返回给主循环。主循环再次调用MoveNext()方法后,协程执行将从上次yield语句暂停的地方继续,并执行下一个yield语句。通过使用自定义的WaitForSeconds类来实现延迟功能,其中通过比较时间来确定协程是否应该继续执行。最后,协程完成后会打印"Coroutine finished"。
请注意,示例中的Coroutine方法只是一个简单的示范,并不适用于所有情况。在实际开发中,可能需要处理更复杂的逻辑和错误处理。使用协程时,需要确保在每个yield语句之间没有太长的延迟,否则会导致主循环被阻塞。
让上面实例程序安全的后台运行
要将上面的实例程序安全地后台运行,可以使用Task类来实现。下面是修改后的代码示例:
using System;
using System.Collections;
using System.Threading.Tasks;
class CoroutineExample
{
static async Task Main(string[] args)
{
IEnumerator coroutine = Coroutine();
while (coroutine.MoveNext())
{
// Do something
await Task.Yield(); // Yield to main loop
}
Console.WriteLine("Coroutine finished");
}
static IEnumerator Coroutine()
{
Console.WriteLine("Coroutine started");
yield return null; // Yield to main loop
Console.WriteLine("Coroutine resumed");
yield return new WaitForSeconds(2); // Yield for 2 seconds
Console.WriteLine("Coroutine resumed");
yield return new WaitForSeconds(3); // Yield for 3 seconds
Console.WriteLine("Coroutine finished");
}
}
class WaitForSeconds
{
private readonly float duration;
private DateTime endTime;
public WaitForSeconds(float seconds)
{
duration = seconds;
endTime = DateTime.Now.AddSeconds(duration);
}
public bool IsFinished()
{
return DateTime.Now >= endTime;
}
}
在Main方法中,我们用async修饰,以便在其中使用await关键字。然后,我们将主循环中的MoveNext()方法调用包装在一个Task.Yield()中,以确保它在后台上下文中执行。这样,协程就会在后台运行,同时允许主循环继续执行。
请注意,这个修改后的代码与之前的代码的逻辑相同。主要的改变是在Main方法中,使用了async修饰,并在主循环中使用了Task.Yield()方法。
进程退出了,并没有保持后台运行
要使程序后台保持运行而不退出,可以使用一个无限循环来实现。下面是修改后的代码示例:
using System;
using System.Collections;
using System.Threading.Tasks;
class CoroutineExample
{
static async Task Main(string[] args)
{
bool running = true;
IEnumerator coroutine = Coroutine();
while (running)
{
if (!coroutine.MoveNext())
{
running = false;
}
await Task.Yield(); // Yield to main loop
}
Console.WriteLine("Coroutine finished");
}
static IEnumerator Coroutine()
{
Console.WriteLine("Coroutine started");
yield return null; // Yield to main loop
Console.WriteLine("Coroutine resumed");
yield return new WaitForSeconds(2); // Yield for 2 seconds
Console.WriteLine("Coroutine resumed");
yield return new WaitForSeconds(3); // Yield for 3 seconds
Console.WriteLine("Coroutine finished");
}
}
class WaitForSeconds
{
private readonly float duration;
private DateTime endTime;
public WaitForSeconds(float seconds)
{
duration = seconds;
endTime = DateTime.Now.AddSeconds(duration);
}
public bool IsFinished()
{
return DateTime.Now >= endTime;
}
}
在Main方法中,我们添加了一个名为"running"的布尔变量,并将初始值设置为true。在每次循环迭代中,我们检查协程是否已经完成(即MoveNext()方法返回false),如果完成则将running变量设置为false,以退出循环并结束程序。
现在,程序将进入一个无限循环,在每次循环迭代中暂停执行以允许其他后台任务运行。这样,主循环将保持活动状态,并且协程将在后台按照定义的逻辑进行执行。
测试发现并没有等待2秒,3秒的过程
对于这种需要等待一段时间的协程,我们需要在主循环中进行时间的轮询,以便在等待的期间继续等待。下面是修改后的代码示例:
using System;
using System.Collections;
using System.Threading.Tasks;
class CoroutineExample
{
static async Task Main(string[] args)
{
bool running = true;
IEnumerator coroutine = Coroutine();
DateTime waitUntil = DateTime.Now;
while (running)
{
if (DateTime.Now >= waitUntil)
{
if (!coroutine.MoveNext())
{
running = false;
}
if (coroutine.Current is WaitForSeconds)
{
var waitForSeconds = (WaitForSeconds)coroutine.Current;
waitUntil = DateTime.Now.AddSeconds(waitForSeconds.Duration);
}
}
await Task.Yield(); // Yield to main loop
}
Console.WriteLine("Coroutine finished");
}
static IEnumerator Coroutine()
{
Console.WriteLine("Coroutine started");
yield return null; // Yield to main loop
Console.WriteLine("Coroutine resumed");
yield return new WaitForSeconds(2); // Yield for 2 seconds
Console.WriteLine("Coroutine resumed");
yield return new WaitForSeconds(3); // Yield for 3 seconds
Console.WriteLine("Coroutine finished");
}
}
class WaitForSeconds
{
public float Duration { get; }
public WaitForSeconds(float seconds)
{
Duration = seconds;
}
}
在这个修改后的代码中,我们引入了一个名为waitUntil
的变量来跟踪等待的时间点。在每次循环迭代中,我们首先检查当前时间是否超过了waitUntil
,如果是,则执行协程的下一个步骤(如果有的话),然后更新waitUntil
来等待一段时间。
WaitForSeconds
类不再使用DateTime
来计算是否结束,而是直接存储等待的持续时间。这样,当WaitForSeconds
被返回时,我们可以根据其持续时间来更新waitUntil
。
现在,程序将在每次主循环迭代时检查等待时间并执行相应的操作。这样,协程将在每次等待时间结束后继续执行。
我不是很理解Yield to main loop
在异步编程中,await Task.Yield()
语句是用于在主循环中让出控制权,以便让其他任务或操作有机会执行。它允许实现异步协程的循环主循环保持活动状态,并允许其他任务在等待期间执行。
通过添加await Task.Yield()
,我们将控制权交还给主循环,使得它可以继续执行其他任务。主循环将在等待期间持续轮询等待时间是否已到,以便在合适的时候继续执行协程。而不是将整个处理器时间用于循环,从而允许其他任务在等待期间运行,提高了整体的效率。
简而言之,await Task.Yield()
允许主循环去做一些其他的事情,而不是一直在等待协程的下一个步骤。它是异步编程中常用的一种机制,有助于提高并发性和性能。
练习的项目文件: