This is (roughly) what I have:
class A
{
public bool IsInUpdate = false;
public void Update()
{
IsInUpdate = true;
//(...do stuff...)
IsInUpdate = false;
}
}
class B
{
A a_inst;
System.Threading.Thread physicsThread = null;
void Draw()
{
physicsThread = new System.Threading.Thread(a_inst.Update);
physicsThread.Start();
}
void Update()
{
while(physicsThread.IsAlive)
{
// Right here there can be cases where physicsThread.IsAlive is true but IsInUpdate is false, how does that happen?
}
(...do stuff...)
}
}
Question is in the comments of the code. Basically the physics thread instance says it's alive but the function it's calling has clearly been finished calling (as can be seen by th开发者_运维百科e bool being set to false).
Any ideas why this happens? All I want to do is make sure the update function in class B does not execute until the threaded update function of class A has executed...
Since IsInUpdate
is simply a public field (and non-volatile
at that), there are no guarantees about what you see; the normal sensible rules about what you see only apply on a single thread, and you have not guarded any of this data. There is also an edge-case around the start condition, but personally I would be using either lock
(if you need to wait for it to complete), or maybe Interlocked
if you just need to know if it is active.
For example:
class A
{
private readonly object syncLock = new object();
public object SyncLock { get { return syncLock; } }
public void Update()
{
lock(SyncLock)
{
//(...do stuff...)
}
}
}
and
void Update()
{
lock(a_inst.SyncLock)
{
(...do stuff...)
}
}
With the above, you are guaranteed that only one thread will have the lock at any time, so if you get to "do stuff" you know that it isn't also running the other Update(). If you need to wait etc there are also Wait()
/ Pulse()
methods against locks, or you can use gates such as ManualResetEvent
/AutoResetEvent
.
Things like lock
also ensure correct memory barriers between the threads, so you see the correct data.
This situation can happen when the Update
function has not been called yet. Just because you have called Start
on the thread doesn't mean it's immediately going to execute it's main function. I'm not 100% sure if there is a slight window of opportunity where the thread is still alive but the main function has finished executing.
Basically you want to have a look at ManualResetEvent
or AutoResetEvent
to signal that your thread has finished working. Alternatively an event you can raise after Update()
has finished and B
can subscribe to might be good enough. Like this:
class A
{
public event EventHandler UpdateFinished;
public void Update()
{
... do work
var handler = UpdateFinished;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
class B
{
public void Draw()
{
a_inst.UpdateFinished += HandleUpdateFinished;
... start your thread
}
private void HandleUpdateFinished(object sender, EventArgs e)
{
... do whatever
}
}
精彩评论