0

I am using a array m_aoAlternatingJobs in various functions of a class. These functions are accessed by different threads. Therefore I am using a Monitor-lock (c++cli) in each function that accesses this array prior to the actual access.

The problem now is, that in one function I am resizing the array. As described by the MSDN documentation, this will create a new array and copy values, so even if I did not use the Monitor (I am doing on each necessary place), I would not get a program crash in any case, but other threads could access the old array (see also here: stackoverflow: Is Array.Resize(..) threadsafe?).

If I am using a Monitor directly on the array object, the Monitor fails to exit with SynchronizationLockException because the Exit is called on the new, unlocked object. But I do have to use a lock to avoid resizing the array while in use on another function. Thus I'm doing the following trick to avoid access to the array while being resized:

void Class1::JobsCount::set (int i_iJobCount)
{
  if (i_iJobCount < 1) i_iJobCount = 1;
  bool bSuccess = false;

  Object^ oRefOld;
  try
  {
    bSuccess = Monitor::TryEnter (m_aoJobs, msc_iMonitorTimeout);
    if (!bSuccess) return;

    oRefOld = m_aoAlternatingJobs;
    Array::Resize (m_aoJobs, i_iJobCount);
  }
  finally
  { if (bSuccess) Monitor::Exit (oRefOld); }
}

I'm not getting an Exception any more, but the more important question is: Will this effectively block access to the array while the resize is being done?

Edit:
I am aware of the possibility of using a separate lock object, thanks for the advices. Nevertheless I would like to receive an answer about the question above.

4
  • 2
    Why you not just create separate lock object? Commented Dec 5, 2014 at 16:22
  • I thought about that in the beginning, but as one object for all members that are accessed by more than 1 thread. I decided not to create that separate object, but in this case it would be really an alternative coming back to that idea. However, I would like to have the question answered. ;-) Commented Dec 5, 2014 at 16:27
  • You'll get away with it. Object reference updates are atomic in .NET and the implied memory barrier in Monitor::Exit() ensures that other threads won't see a stale reference to the old array. But, jeez, minus 100 elegant points and pity the poor sap that needs to maintain this code some day. Doing it right doesn't cost anything. Commented Dec 5, 2014 at 17:27
  • @HansPassant: It likely was not the most elegant approach to the problem, but the goal was to get an answer whether this is working or not. As it is not (see below), I won't use it and I can save my time adding comments. Actually, I have added a comment now why it's not working so that I don't have that idea again in the future. Commented Dec 5, 2014 at 17:55

3 Answers 3

1

Correct me if I am wrong but I think you are misunderstanding what Monitor::Lock does. Even if you call Monitor::Lock(m_aoJobs), it is not going to prevent a concurrent access from an other piece of code that do not call Monitor::Lock prior to accessing the array.

The solution is simple: have a dummy object that is only used as an argument to Monitor::Lock. Even if the array is resized, that object will continue to exist and it is not going to cause a crash on Monitor::Exit. Then have all pieces of code accessing the array first call Monitor::Lock with the same dummy object and you are good to go.

try {
    Monitor::Lock(m_lock);
    Array::Resize (m_aoJobs, i_iJobCount);
}
finally { Monitor::Exit(m_lock); }

m_lock is the dummy object that should be created in the constructor of the class.

m_lock = gcnew Object();
Sign up to request clarification or add additional context in comments.

3 Comments

I can reassure you, I know what Monitor does. ;-) See 3rd sentence of my post: "Therefore I am using a Monitor-lock (c++cli) in each function that accesses this array prior to the actual access." Also, thanks for the advice, I could do that, see 1st and 2nd comment on my post. But I would like to know, whether the lock works. It has become more of a question about the working principle for me, as your answer is definitely giving me a solution.
Sorry about that, I should have red more carefully.
No problem, can happen to everyone. BTW: I am using your solution now, see answer of Taukita.
0

No. This approach not thread-safe. Look at this scenario:

  1. Your function Class1::JobsCount::set in thread 1 locks m_aoJobs

  2. Function 1 in thread 2 try to locks m_aoJobs, but cannot do it and wait

  3. Your function Class1::JobsCount::set in thread 1 replace m_aoJobs with new one and unlock old m_aoJobs

  4. Function 2 in thread 3 lock new m_aoJobs

  5. Function 1 in thread 2 lock old m_aoJobs and modify new one!

This scenario unlikely, but possible.

1 Comment

So, when step 2 comes up and thread 2 calls the Monitor.TryEnter, it will do on the old array. But after unlocking the array in step 3, the thread 2 continues and works with the new array in the next line, which means, thread 2 works on an array which he has no lock for. That's actually exactly what I dit NOT intend, and it actually CAN happen. Thank you a lot for that explanation! I am going to use an "independent" lock now.
0

The trick you came with might prevent concurrent accesses between the different parts of you code but it is not going to prevent Array::Resize from working with the array at the same time as another part of your code. It all depends on how Array::Resize is implemented. If the method sets the new value of m_aoJobs just before returning, once the elements of the array have all been copied, then it is going to work. It's probably what is does. However, since it is an implementation detail (unless it is part of the specification of Array::Resize), I would not count on this behavior; it might change in the future.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.