Reputation: 21512
I've got a class. It has a method that does a lot of work. I would like to not have the program hang while it does the work. I understand yield
will do this for me.
void Start() {
DoWork(10,10);
}
void DoWork (int x, int y) {
for (int i=0; i < x; i++) {
for (int j=0; j < y; j++) {
// Stuff
}
}
}
If I add a yield
like this
void Start() {
DoWork(10, 10);
}
IEnumerable DoWork (int x, int y) {
for (int i=0; i < x; i++) {
for (int j=0; j < y; j++) {
// Stuff
}
Debug.Log (1);
yield return null;
}
}
None of the work gets done and on top of that I see no log statements at all.
How do I yield
my code so the program doesn't freeze?
Upvotes: 0
Views: 1647
Reputation: 3629
C# yield in Unity works just like C# yield always does. Unity does not influence this in any way.
yield
is a keyword that is used to allow enumeration over a set of return values.
IEnumerator<int> MyEnumerationMethod()
{
yield return 5;
yield return 1;
yield return 9;
yield return 4;
}
void UserMethod1()
{
foreach (int retVal in MyEnumerationMethod())
Console.Write(retVal + ", ");
// this does print out 5, 1, 9, 4,
}
void UserMethod2()
{
IEnumerator<int> myEnumerator = MyEnumerationMethod();
while (myEnumerator.MoveNext())
Console.Write(myEnumerator.Current + ", ");
// this does print out 5, 1, 9, 4,
}
UserMethod1() and UserMethod2() are pretty much the same. UserMethod1() is just the C# syntactic sugar version of UserMethod2().
Unity uses this language feature to implement Coroutines:
When you call StartCoroutine()
and pass it an IEnumerator
, Unity stores this enumerator and calls MoveNext()
for the first time. This will cause MyEnumerationMethod()
to be called and executed up until the first yield return
. At this point, MoveNext()
returns and the first result (5) can be retrieved by looking at the Current
property of the enumerator.
Now, Unity regularly checks the Current
property and - depending on its value - decides whether the time has come to call MoveNext()
again. The value of Current
might be an instance of WaitForEndOfFrame
, an instance of WWW
or whatever, and depending on that the time, MoveNext()
is called is decided.
Once MoveNext()
is called again, execution of MyEnumerationMethod()
will be continued at the point where it was interrupted last time, and executes until the next yield return
is executed. And so on.
That's all there is to yield, and to Coroutines in Unity.
Upvotes: 0
Reputation: 10701
This is Unity3D engine so your coroutine needs to return IEnumerator to be valid:
void Start() {
StartCoroutine(DoWork(10, 10));
}
IEnumerator DoWork (int x, int y) {
for (int i=0; i < x; i++) {
for (int j=0; j < y; j++) {
// Stuff
}
Debug.Log (1);
yield return null;
}
}
This is in no way multithreading. It is run just like an update once per frame between the Update and the LateUpdate except if you use
yield return new WaitForEndOfFrame();
then it is postponed until after the rendering process. What it does is create a new object of type Coroutine and place it on the calling MonoBehaviour stack of coroutines.
This works as a method that performs some repetitive action but always return to the main program when hitting a yield. It will then catch back from there on the next frame.
Upvotes: 2
Reputation: 10032
You need to use the StartCoroutine method:
void Start() {
StartCoroutine(DoWork(10, 10));
}
IEnumerator DoWork (int x, int y) {
// (A)
yield return null;
// (B)
for (int i=0; i < x; i++) {
for (int j=0; j < y; j++) {
// Stuff
}
Debug.Log (1);
yield return null;
// (C)
}
}
Yur code is executed piece by piece where delimiter of steps is the yield operator, i.e. when Framework calls MoveNext() the first time - the code (A) will be executed, when it calls MoveNext() second time - the code (B) will be executed, then code (C), and so on and so forth.
Upvotes: 2
Reputation: 4410
Yield
keyword is used for lazy loading/computation support in C#.
Try doing:
var result = DoWork().ToList();
This forces an evaluation of the DoWork() method and you will see the logging taking place.
Upvotes: 0
Reputation: 38130
When you add a yield
statement, the compiler actually generates a private class that acts as a state machine that implements IEnumerable
. As such none of the code wrapped up from the original method will be called unless you enumerate the result of the method - in your example, you're throwing away the return value, so nothing would happen.
Upvotes: 1