Multithreading – is the method pointer assignment thread safe?

Example:

Suppose I have the following clues (please do not consider the contents used in the thread context execution method of this example, for illustration only):

type
  TSampleThread = class(TThread)
  private
    FOnNotify: TNotifyEvent;
  protected
    procedure Execute; override;
  public
    property OnNotify: TNotifyEvent read FOnNotify write FOnNotify;
  end;

implementation

procedure TSampleThread.Execute;
begin
  while not Terminated do
  begin
    if Assigned(FOnNotify) then
      FOnNotify(Self); // <- this method can be called anytime
  end;
end;

Then suppose I want to change the method of onnotify event from the main thread at any time This main thread implements the event handling method of threadnotify method:

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    FSampleThread: TSampleThread;
    procedure ThreadNotify(Sender: TObject);
  end;

implementation

procedure TForm1.ThreadNotify(Sender: TObject);
begin
  // do something; unimportant for this example
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FSampleThread.OnNotify := nil; // <- can this be changed anytime ?
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  FSampleThread.OnNotify := ThreadNotify; // <- can this be changed anytime ?
end;

Question:

Can you change a method that can be called from a worker thread in another thread context at any time? Is it safe to do the security shown in the above example?

I'm not sure if this is absolutely safe, at least because the method pointer is actually a pair of pointers, I don't know if I can treat it as an atomic operation

Solution

No, it's not thread safe because the operation will never be "atomic" Tnotifyevent consists of two pointers that will not be assigned at the same time: one pointer will be assigned and the other will be assigned

The 32-bit assembler generated for tnotifyevent allocation consists of two different assembler instructions, as follows:

MOV [$00000000],Object
MOV [$00000004],MethodPointer

If it is a single pointer, you will have some options because the operation is atomic: the options you choose depend on the power of the CPU's memory model:

>If the CPU supports the "sequential consistency" mode, any read that occurs after writing to memory will see the new value If so, you can simply write down your value without memory barriers or using interlocking methods. > If the CPU can reorder stores and loads more easily, you need a "memory barrier" If so, the simplest solution is to use the interlockedexchangepointer

Unfortunately, I don't know how powerful the current Intel CPU memory model is There is some circumstantial evidence that some reordering may occur, and those using interlock may be recommended, but I don't see an explicit statement from Intel or another

Evidence:

>Modern CPUs use "prefetching" – this means some sort of load / store reordering. > SSE introduces the specific instructions for processing CPU cache

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>