This occurs because there is only one i variable in the original code as for does not introduce a new variable "each loop" and the same variable is closed over in all the closures! As such, the same (i) variable is assigned the last value (3) before any of the functions are executed after the loop.
Compare with the following code which ensures a new variable to bind in each closure (this is one rare case where I use underscores for a local variable to show "something funny" is happening):
for (int _i = 0; _i < tmp.Length; _i++)
{
    int i = _i; // NEW/fresh variable, i, introduced each loop
    tmp[i] = () => Console.WriteLine(i);
}
This behavior (and complaints against) is discussed in detail in Is there a reason for C#'s reuse of the variable in a foreach? - for and foreach have this same "issue" in C#4 and before, which is "fixed" for foreach in C#5.
I think it is fair to say that all regret that decision. This is one of the worst "gotchas" in C#, and we are going to take the breaking change to fix it. In C# 5 the foreach loop variable will be logically inside the body of the loop, and therefore closures will get a fresh copy every time. - Eric Lippert