سؤال

قمت بإنشاء فئة تفتح منفذ COM ومقبض عمليات القراءة والكتابة متداخلة. أنه يحتوي على اثنين من المواضيع المستقلة - واحد يقرأ وواحد يكتب البيانات. كلاهما يسمي إجراءات Onxxx (مثل Onread أو Onwrite) إخطار حول عملية القراءة أو الكتابة المنتهية.

فيما يلي مثال قصير عن فكرة كيفية عمل الخيوط:

  TOnWrite = procedure (Text: string);

  TWritingThread = class(TThread)
  strict private
    FOnWrite: TOnWrite;
    FWriteQueue: array of string;
    FSerialPort: TAsyncSerialPort;
  protected
    procedure Execute; override;
  public
    procedure Enqueue(Text: string);
    {...}
  end;

  TAsyncSerialPort = class
  private
    FCommPort: THandle;
    FWritingThread: TWritingThread;
    FLock: TCriticalSection;
    {...}
  public
    procedure Open();
    procedure Write(Text: string);
    procedure Close();
    {...}
  end;

var
  AsyncSerialPort: TAsyncSerialPort;

implementation

{$R *.dfm}

procedure OnWrite(Text: string);
begin
  {...}
  if {...} then
    AsyncSerialPort.Write('something');
  {...}
end;

{ TAsyncSerialPort }

procedure TAsyncSerialPort.Close;
begin
  FLock.Enter;
  try
    FWritingThread.Terminate;
    if FWritingThread.Suspended then
      FWritingThread.Resume;
    FWritingThread.WaitFor;
    FreeAndNil(FWritingThread);

    CloseHandle(FCommPort);
    FCommPort := 0;
  finally
    FLock.Leave;
  end;
end;

procedure TAsyncSerialPort.Open;
begin
  FLock.Enter;
  try
    {open comm port}
    {create writing thread}
  finally
    FLock.Leave;
  end;
end;

procedure TAsyncSerialPort.Write(Text: string);
begin
  FLock.Enter;
  try
    {add Text to the FWritingThread's queue}
    FWritingThread.Enqueue(Text);
  finally
    FLock.Leave;
  end;
end;

{ TWritingThread }

procedure TWritingThread.Execute;
begin
  while not Terminated do
  begin
    {GetMessage() - wait for a message informing about a new value in the queue}
    {pop a value from the queue}
    {write the value}
    {call OnWrite method}
  end;
end;

عند إلقاء نظرة على إجراء إغلاق ()، سترى أنه يدخل القسم الحرج، إنهاء مؤشر ترابط الكتابة ثم ينتظره حتى ينتهي. بسبب حقيقة أن مؤشر ترابط الكتابة يمكن أن يؤدي إلى كتابة قيمة جديدة عند استدعاء طريقة Onwrite، سيحاول إدخال نفس القسم الحرج عند الاتصال بإجراء الكتابة () لفئة TasyncSerialPort.

وهنا لدينا حالة توقف تام. تم إدخال مؤشر الترابط الذي يسمى الأسلوب الإغلاق () القسم الحرج ثم ينتظر إغلاق مؤشر ترابط الكتابة، بينما في نفس الوقت الذي ينتظر الخيط من أجل إطلاق سراح القسم الحرج الذي سيتم تحريره.

لقد كنت أفكر لفترة طويلة ولم أتمكن من العثور على حل لهذه المشكلة. الشيء هو أنني أود أن أكون متأكدا من عدم وجود مؤشر ترابط القراءة / الكتابة على قيد الحياة عندما يتم ترك الطريقة الإغلاق ()، مما يعني أنني لا أستطيع ضبط العلم المستغرق من هذه المواضيع والمغادرة.

كيف يمكنني حل المشكلة؟ ربما يجب علي تغيير نهجي لمعالجة المنفذ التسلسلي غير متزامن؟

شكرا لنصيحتك مقدما.

ماريوس.

--------- تعديل ----------
ماذا عن هذا الحل؟

procedure TAsyncSerialPort.Close;
var
  lThread: TThread;
begin
  FLock.Enter;
  try
    lThread := FWritingThread;
    if Assigned(lThread) then
    begin
      lThread.Terminate;
      if lThread.Suspended then
        lThread.Resume;
      FWritingThread := nil;
    end;

    if FCommPort <> 0 then
    begin
      CloseHandle(FCommPort);
      FCommPort := 0;
    end;
  finally
    FLock.Leave;
  end;

  if Assigned(lThread) then
  begin
    lThread.WaitFor;
    lThread.Free;
  end;
end;

إذا كان تفكيري صحيحا، فيجب أن يخلص هذا إلى القضاء على مشكلة الجمود. لسوء الحظ، ومع ذلك، أغلق مقبض منفذ Comm قبل إغلاق مؤشر ترابط الكتابة. هذا يعني أنه عندما يستدعي أي طريقة تأخذ مقبض منفذ Comm كأحد حججه (مثل الكتابة والقراءة وينتظره) يجب رفع استثناء في هذا الموضوع. هل يمكن أن أكون متأكدا من أنه إذا قبضت على هذا الاستثناء في هذا الموضوع، فلن يؤثر ذلك على عمل التطبيق بأكمله؟ قد يبدو هذا السؤال غبي، لكنني أعتقد أن بعض الاستثناءات قد تتسبب في إغلاق نظام التشغيل التطبيق الذي تسبب في ذلك، أليس كذلك؟ هل يجب أن تقلق بشأن ذلك في هذه الحالة؟

هل كانت مفيدة؟

المحلول

نعم، يجب عليك إعادة النظر في نهجك. تتوفر العمليات غير المتزامنة تماما للقضاء على الحاجة إلى المواضيع. إذا كنت تستخدم الخيوط، فاستخدم مكالمات (حظر) متزامن. إذا كنت تستخدم العمليات غير المتزامنة، فالتعامل مع كل شيء في مؤشر ترابط واحد - ليس بالضرورة الترابط الرئيسي، لكنه لا معنى له المنظمة البحرية الدولية للقيام بالإرسال والاستقبال في مؤشرات ترابط مختلفة.

هناك طرق بالطبع حول مشكلة المزامنة الخاصة بك، لكنني أفضل تغيير التصميم.

نصائح أخرى

يمكنك أن تأخذ القفل من الإغلاق. بحلول الوقت الذي تقوم فيه بإرجاع من Waitfor، لاحظت هيئة الخيط أنه تم إنهاءها، أكملت الحلقة الأخيرة، وانتهت.

إذا كنت لا تشعر بالسعادة في القيام بذلك، فيمكنك نقل تحديد القفل قبل Freeandnil. يتيح هذا صراحة أن آليات إيقاف تشغيل مؤشر الترابط تعمل قبل تطبيق القفل (لذلك لن تضطر إلى التنافس مع أي شيء من أجل القفل)

تعديل:

(1) إذا كنت ترغب أيضا في إغلاق مقبض CUMS القيام بذلك بعد الحلقة في تنفيذ، أو في Destructor المؤشرات.

(2) آسف، ولكن حل التحرير الخاص بك هو فوضى فظيعة. إنهاء وينتظر سيفعل كل ما تحتاجه، بأمان تماما.

يبدو أن المشكلة الرئيسية أنها تضع محتوى كامل من قريب في قسم حرج. أنا متأكد تقريبا (ولكن عليك التحقق من المستندات) أن TTHREAD.TERMINES و TTHREAD.WAITFOR من آمنة للاتصال من خارج القسم. من خلال سحب هذا الجزء خارج القسم الحرج، سوف تحل محل الجمود.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top