2012-12-01 24 views
18

वर्तमान में मैं फ़ाइलों को स्थानांतरित करने के लिए डेल्फी XE3 क्लाइंट/सर्वर एप्लिकेशन पर काम कर रहा हूं (इंडी एफ़टीपी घटकों के साथ)। क्लाइंट भाग एक फ़ोल्डर की निगरानी करता है, अंदर फ़ाइलों की एक सूची प्राप्त करता है, उन्हें सर्वर पर अपलोड करता है और मूल को हटा देता है। अपलोडिंग एक अलग थ्रेड द्वारा की जाती है, जो फ़ाइलों को एक-एक करके संसाधित करती है। फाइलें 0 से कुछ हज़ार तक हो सकती हैं और उनके आकार भी बहुत भिन्न होते हैं।मल्टीथ्रेडेड फ़ाइल अपलोड सिंक्रनाइज़ेशन

यह ओएसएक्स और विंडोज दोनों के लिए संकलित एक फायरमोनकी ऐप है, इसलिए मुझे ओमनी थ्रेड लाइब्रेरी के बजाय टीटीएचड का उपयोग करना पड़ा, जिसे मैंने पसंद किया था। मेरा ग्राहक रिपोर्ट करता है कि एप्लिकेशन यादृच्छिक रूप से जमा हो जाता है। मैं इसे डुप्लिकेट नहीं कर सका, लेकिन चूंकि मेरे पास टीटीएचड के साथ इतना अनुभव नहीं है, इसलिए मैंने कहीं डेडलॉक स्थिति डाली होगी। मैंने काफी सारे उदाहरण पढ़े हैं, लेकिन मुझे अभी भी कुछ मल्टीथ्रेड विनिर्देशों के बारे में निश्चित नहीं है।

ऐप संरचना सरल है:
मुख्य धागे में एक टाइमर फ़ोल्डर की जांच करता है और प्रत्येक फ़ाइल के बारे में जानकारी रिकॉर्ड में प्राप्त करता है, जो एक सामान्य टीएलिस्ट में जाता है। यह सूची फाइलों, आकार, प्रगति के नामों के बारे में जानकारी रखती है, भले ही फ़ाइल पूरी तरह से अपलोड हो या फिर से प्रयास किया जाए। प्रगति सलाखों, आदि के साथ एक ग्रिड में प्रदर्शित सभी। यह सूची केवल मुख्य धागे द्वारा उपयोग की जाती है। उसके बाद सूची से आइटम को AddFile विधि (नीचे कोड) को कॉल करके थ्रेड पर भेजा जाता है। धागा सभी फ़ाइलों को एक थ्रेड-सुरक्षित कतार में संग्रहीत करता है जैसे http://delphihaven.wordpress.com/2011/05/06/using-tmonitor-2/
जब फ़ाइल अपलोड की जाती है तो अपलोडर थ्रेड सिंक्रनाइज़ करने के लिए कॉल के साथ मुख्य थ्रेड को सूचित करता है।
मुख्य थ्रेड समय-समय पर वर्तमान फ़ाइल प्रगति की जांच करने और इसे प्रदर्शित करने के लिए अपलोडर.गेटप्रकाश विधि को कॉल करता है। यह फ़ंक्शन वास्तव में थ्रेड-सुरक्षित नहीं है, लेकिन क्या यह डेडलॉक का कारण बन सकता है, या केवल गलत डेटा लौटाया जा सकता है?

प्रगति जांच करने के लिए एक सुरक्षित और कारगर तरीका क्या होगा?

तो क्या यह दृष्टिकोण ठीक है या मैंने कुछ याद किया है? आप यह कैसे करेंगे?
उदाहरण के लिए मैं फ़ोल्डर सामग्री को पढ़ने के लिए एक नया धागा बनाने के बावजूद। इसका मतलब है कि मैं जिस टीएलआईस्ट का उपयोग करता हूं उसे थ्रेड-सुरक्षित बनाया जाना चाहिए, लेकिन जीयूआई ग्रिड में प्रदर्शित जानकारी को रीफ्रेश करने के लिए इसे हर समय एक्सेस करना होगा। क्या सभी सिंक्रनाइज़ेशन सिर्फ जीयूआई को धीमा नहीं करेंगे?

यदि कोई इसे देखना चाहता है तो मैंने नीचे सरलीकृत कोड पोस्ट किया है। यदि नहीं, तो मुझे सामान्य रूप से उपयोग करने के बारे में कुछ राय सुनकर खुशी होगी। मुख्य लक्ष्य ओएसएक्स और विंडोज दोनों पर काम करना है; सभी फाइलों और वर्तमान की प्रगति के बारे में जानकारी प्रदर्शित करने में सक्षम होने के लिए; और फाइलों की संख्या और आकार के बावजूद उत्तरदायी होने के लिए।

यह अपलोडर धागा का कोड है। मैं आसानी से पढ़ने के लिए इसके बारे में कुछ निकाल दिया है: मुख्य थ्रेड कि अपलोड करने वाले के साथ बातचीत की

type 
    TFileStatus = (fsToBeQueued, fsUploaded, fsQueued); 
    TFileInfo = record 
    ID: Integer; 
    Path: String; 
    Size: Int64; 
    UploadedSize: Int64; 
    Status: TFileStatus; 
    end; 

    TUploader = class(TThread) 
    private 
    FTP: TIdFTP; 
    fQueue: TThreadedQueue<TFileInfo>; 
    fCurrentFile: TFileInfo; 
    FUploading: Boolean; 
    procedure ConnectFTP; 
    function UploadFile(aFileInfo: TFileInfo): String; 
    procedure OnFTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64); 
    procedure SignalComplete; 
    procedure SignalError(aError: String); 
    protected 
    procedure Execute; override; 
    public 
    property Uploading: Boolean read FUploading; 
    constructor Create; 
    destructor Destroy; override; 
    procedure Terminate; 
    procedure AddFile(const aFileInfo: TFileInfo); 
    function GetProgress: TFileInfo; 
    end; 

procedure TUploader.AddFile(const aFileInfo: TFileInfo); 
begin 
    fQueue.Enqueue(aFileInfo); 
end; 

procedure TUploader.ConnectFTP; 
begin 
    ... 
    FTP.Connect; 
end; 

constructor TUploader.Create; 
begin 
    inherited Create(false); 
    FreeOnTerminate := false; 
    fQueue := TThreadedQueue<TFileInfo>.Create; 
    // Create the TIdFTP and set ports and other params 
    ... 
end; 

destructor TUploader.Destroy; 
begin 
    fQueue.Close; 
    fQueue.Free; 
    FTP.Free; 
    inherited; 
end; 

// Process the whole queue and inform the main thread of the progress 
procedure TUploader.Execute; 
var 
    Temp: TFileInfo; 
begin 
    try 
    ConnectFTP; 
    except 
    on E: Exception do 
     SignalError(E.Message); 
    end; 

    // Use Peek instead of Dequeue, because the item should not be removed from the queue if it fails 
    while fQueue.Peek(fCurrentFile) = wrSignaled do 
    try 
     if UploadFile(fCurrentFile) = '' then 
     begin 
     fQueue.Dequeue(Temp); // Delete the item from the queue if succesful 
     SignalComplete; 
     end; 
    except 
     on E: Exception do 
     SignalError(E.Message); 
    end; 
end; 

// Return the current file's info to the main thread. Used to update the progress indicators 
function TUploader.GetProgress: TFileInfo; 
begin 
    Result := fCurrentFile; 
end; 

// Update the uploaded size for the current file. This information is retrieved by a timer from the main thread to update the progress bar 
procedure TUploader.OnFTPWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64); 
begin 
    fCurrentFile.UploadedSize := AWorkCount; 
end; 

procedure TUploader.SignalComplete; 
begin 
    Synchronize(
    procedure 
    begin 
     frmClientMain.OnCompleteFile(fCurrentFile); 
    end); 
end; 

procedure TUploader.SignalError(aError: String); 
begin 
    try 
    FTP.Disconnect; 
    except 
    end; 
    if fQueue.Closed then 
    Exit; 

    Synchronize(
    procedure 
    begin 
     frmClientMain.OnUploadError(aError); 
    end); 
end; 

// Clear the queue and terminate the thread 
procedure TUploader.Terminate; 
begin 
    fQueue.Close; 
    inherited; 
end; 

function TUploader.UploadFile(aFileInfo: TFileInfo): String; 
begin 
    Result := 'Error'; 
    try 
    if not FTP.Connected then 
     ConnectFTP; 
    FUploading := true; 
    FTP.Put(aFileInfo.Path, ExtractFileName(aFileInfo.Path));  
    Result := ''; 
    finally 
    FUploading := false; 
    end; 
end; 

और भागों:

...... 
// Main form 
    fUniqueID: Integer; // This is a unique number given to each file, because there might be several with the same names(after one is uploaded and deleted) 
    fUploader: TUploader;   // The uploader thread 
    fFiles: TList<TFileInfo>; 
    fCurrentFileName: String;  // Used to display the progress 
    function IndexOfFile(aID: Integer): Integer; //Return the index of the record inside the fFiles given the file ID 
    public 
    procedure OnCompleteFile(aFileInfo: TFileInfo); 
    procedure OnUploadError(aError: String); 
    end; 

// This is called by the uploader with Synchronize 
procedure TfrmClientMain.OnUploadError(aError: String); 
begin 
    // show and log the error 
end; 

// This is called by the uploader with Synchronize 
procedure TfrmClientMain.OnCompleteFile(aFileInfo: TFileInfo); 
var 
    I: Integer; 
begin 
    I := IndexOfFile(aFileInfo.ID); 
    if (I >= 0) and (I < fFiles.Count) then 
    begin 
    aFileInfo.Status := fsUploaded; 
    aFileInfo.UploadedSize := aFileInfo.Size; 
    FFiles.Items[I] := aFileInfo; 
    Inc(FFilesUploaded); 
    TFile.Delete(aFileInfo.Path); 
    colProgressImg.UpdateCell(I); 
    end; 
end; 

procedure TfrmClientMain.ProcessFolder; 
var 
    NewFiles: TStringDynArray; 
    I, J: Integer; 
    FileInfo: TFileInfo; 
begin 
    // Remove completed files from the list if it contains more than XX files 
    while FFiles.Count > 1000 do 
     if FFiles[0].Status = fsUploaded then 
     begin 
     Dec(FFilesUploaded); 
     FFiles.Delete(0); 
     end else 
     Break; 

    NewFiles := TDirectory.GetFiles(WatchFolder, '*.*',TSearchOption.soAllDirectories); 
    for I := 0 to Length(NewFiles) - 1 do 
    begin 
      FileInfo.ID := FUniqueID; 
      Inc(FUniqueID); 
      FileInfo.Path := NewFiles[I]; 
      FileInfo.Size := GetFileSizeByName(NewFiles[I]); 
      FileInfo.UploadedSize := 0; 
      FileInfo.Status := fsToBeQueued; 
      FFiles.Add(FileInfo); 

     if (I mod 100) = 0 then 
     begin 
     UpdateStatusLabel; 
     grFiles.RowCount := FFiles.Count; 
     Application.ProcessMessages; 
     if fUploader = nil then 
      break; 
     end; 
    end; 

    // Send the new files and resend failed to the uploader thread 
    for I := 0 to FFiles.Count - 1 do 
     if (FFiles[I].Status = fsToBeQueued) then 
     begin 
     if fUploader = nil then 
      Break; 
     FileInfo := FFiles[I]; 
     FileInfo.Status := fsQueued; 
     FFiles[I] := FileInfo; 
     SaveDebug(1, 'Add: ' + ExtractFileName(FFiles[I].Path)); 
     FUploader.AddFile(FFiles[I]); 
     end; 
end; 

procedure TfrmClientMain.tmrGUITimer(Sender: TObject); 
var 
    FileInfo: TFileInfo; 
    I: Integer; 
begin 
    if (fUploader = nil) or not fUploader.Uploading then 
    Exit; 
    FileInfo := fUploader.GetProgress; 
    I := IndexOfFile(FileInfo.ID); 
    if (I >= 0) and (I < fFiles.Count) then 
    begin 
    fFiles.Items[I] := FileInfo; 
    fCurrentFileName := ExtractFileName(FileInfo.Path); 
    colProgressImg.UpdateCell(I); 
    end; 
end; 

function TfrmClientMain.IndexOfFile(aID: Integer): Integer; 
var 
    I: Integer; 
begin 
    Result := -1; 
    for I := 0 to FFiles.Count - 1 do 
    if FFiles[I].ID = aID then 
     Exit(I); 
end; 
+0

मुझे यकीन नहीं है और आपने परीक्षण नहीं किया है .. लेकिन क्या आपने एक TIdAntiFreeze जोड़ने की कोशिश की और जांच की कि क्या व्यवहार समान है? (FMX.IdAntiFreeze) – Whiler

+2

TIdAntiFreeze को मुख्य थ्रेड (उदा। फॉर्म पर गिराए गए) से इंडी घटक का उपयोग करते समय जीयूआई को ठंडा करने से रोकने के लिए डिज़ाइन किया गया है। मैं इसे एक अलग थ्रेड में उपयोग करता हूं इसलिए मुझे नहीं लगता कि इससे कैसे मदद मिलेगी। कम से कम जहां तक ​​मुझे पता है ... – VGeorgiev

+0

पहली बार देखो, आपकी त्रुटि हैंडलिंग मेरे लिए गलत लगती है। उदाहरण के लिए, निष्पादन विधि में, यदि कनेक्टफ़टीपी कॉल विफल हो जाता है, तो आप अपवाद _ त्रुटि (त्रुटि के बारे में सूचित करने के बाद), और आप अभी भी अपलोडफाइल पर कॉल जारी करते हैं। IMHO आपको _clean_ करना होगा, और थ्रेड को FatalException के साथ मरने दें या निष्पादन विधि के अंदर अपवाद को सही तरीके से संभाल लें, उदाहरण के लिए, कनेक्शन को फिर से प्रयास करना, शायद त्रुटि के प्रकार के आधार पर। दूसरी ओर, यदि आपके पास मुख्य धागे में एक सूची है, तो मैं यह देखने में असफल रहा कि आपको व्यक्तिगत धागे में कतार की आवश्यकता क्यों है। – jachguate

उत्तर

0

यह समस्या नहीं हो सकता है, लेकिन TFileInfo एक रिकार्ड है।

इसका मतलब है कि जब एक (गैर कॉन्स/var) पैरामीटर के रूप में पारित किया जाता है, तो इसकी प्रतिलिपि बनाई जाती है। इसके परिणामस्वरूप रिकॉर्ड में तारों जैसी चीजों के साथ समस्याएं हो सकती हैं, जब रिकॉर्ड की प्रतिलिपि बनाई जाती है तो संदर्भ संख्याएं अपडेट नहीं होती हैं।

कोशिश करने की एक बात यह होगी कि इसे कक्षा बनाएं और पैरामीटर के रूप में एक उदाहरण पास करें (यानी ढेर पर डेटा के लिए एक सूचक)।

थ्रेड 32 बिट सिस्टम पर Int64 के (जैसे आपके आकार मान) साझा करने के लिए कुछ और देखने के लिए कुछ और है।

इन्हें अद्यतन/पढ़ना परमाणु रूप से & नहीं किया गया है, आपके पास कोई विशिष्ट सुरक्षा नहीं है, इसलिए धागे के कारण ऊपरी और निचले 32-बिट्स को मिटाने के लिए मूल्य के पढ़ने के लिए संभव है। (उदाहरण के लिए ऊपरी 32 बिट्स लिखें, ऊपरी 32 बिट लिखें, कम 32 बिट लिखें, & विभिन्न धागे में लिखने के साथ लोअर 32 बिट पढ़ें। यह संभवतः उन समस्याओं को उत्पन्न नहीं कर रहा है जो आप देख रहे हैं और जब तक आप 4 जीबी के फाइल ट्रांसफर के साथ काम नहीं कर रहे हैं, तो आपको किसी भी मुद्दे का कारण बनने की संभावना नहीं है।

0

डेडलॉक्स निश्चित रूप से स्पॉट करना मुश्किल है, लेकिन यह समस्या हो सकती है। आपके कोड में, मैंने नहीं देखा कि आपने एनक्यू, पिक या डेक्यू में कोई टाइमआउट जोड़ा है - जिसका अर्थ यह है कि यह अनंत का डिफ़ॉल्ट लेगा।

एनक्यू में इस पंक्ति में है - यानी, किसी भी सिंक्रनाइज़ेशन ऑब्जेक्ट की तरह, यह तब तक अवरुद्ध हो जाएगा जब तक कि एंटर पूर्ण नहीं होता है (यह मॉनिटर लॉक करता है) या टाइमआउट होता है (क्योंकि आपके पास टाइमआउट नहीं है, यह प्रतीक्षा करेगा केवल आप वास्तव में आइटम निकाल नहीं है - हमेशा के लिए)

TSimpleThreadedQueue.Enqueue(const Item: T; Timeout: LongWord): TWaitResult; 
...  
if not TMonitor.Enter(FQueue, Timeout) 

मैं भी इस धारणा है कि आप अपने आप को तिरछी नज़र कार्यान्वित विपंक्ति के आधार पर बनाने के लिए जा रहा हूँ।

अपनी ही टाइमआउट लागू करने के लिए प्रकट होता है कि - हालांकि, आप अभी भी निम्नलिखित:

function TSimpleThreadedQueue.Peek/Dequeue(var Item: T; Timeout: LongWord): TWaitResult; 
... 
if not TMonitor.Enter(FQueue, Timeout) 

कहाँ टाइमआउट अनंत है - तो, ​​अगर आप के लिए यह एक अनंत के साथ संकेत दिया जाने की प्रतीक्षा झांकना विधि में हैं टाइमआउट, फिर आप एक थ्रेड को अवरुद्ध किए बिना उस थ्रेड को अवरुद्ध किए बिना किसी दूसरे थ्रेड से कुछ को एन्क्यू नहीं कर सकते हैं।

यहाँ TMonitor

Enter locks the monitor object with an optional timeout (in ms) value. 
Enter without a timeout will wait until the lock is obtained. 
If the procedure returns it can be assumed that the lock was acquired. 
Enter with a timeout will return a boolean status indicating whether or 
not the lock was obtained (True) or the attempt timed out prior to 
acquire the lock (False). Calling Enter with an INFINITE timeout 
is the same as calling Enter without a timeout. 

से टिप्पणी का एक टुकड़ा कार्यान्वयन डिफ़ॉल्ट रूप से अनंत का उपयोग करता है, और एक TMonitor.Spinlock मूल्य प्रदान नहीं की है, जब तक यह FQueue वस्तु प्राप्त कर सकते हैं कि धागा को अवरुद्ध कर देगा के बाद से है ।

मेरे सुझाव इस प्रकार अपने कोड को बदलने के लिए होगा:

// Use Peek instead of Dequeue, because the item should not be removed from the queue if it fails 
    while true do 
    case fQueue.Peek(fCurrentFile,10) 
     wrSignaled: 
     try 
      if UploadFile(fCurrentFile) = '' then 
      begin 
      fQueue.Dequeue(Temp); // Delete the item from the queue if succesful 
      SignalComplete; 
      end; 
     except 
      on E: Exception do 
      SignalError(E.Message); 
     end; 
     wrTimeout: sleep(10); 
     wrIOCompletion, 
     wrAbandoned, 
     wrError: break; 
    end; //case 

इस तरह, झांकना को कतारबद्ध यह अधिग्रहण और से फ़ाइल जोड़ने के लिए के लिए अनिश्चित काल के लिए FQueue पर ताला पकड़ नहीं होगा, एक विंडो छोड़े मुख्य (यूआई) धागा।

+0

विस्तृत उत्तर के लिए धन्यवाद। मैं मानता हूं कि दो टीएमओनिटर। एंटर() लाइनें डेडलॉक का कारण बन सकती हैं। TSimpleThreadedQueue.Peek/Dequeue में TMonitor.Enter() एक TMonitor.Wait() के बाद आता है। अगर मैं इसे सही ढंग से समझता हूं, तो प्रतीक्षा अस्थायी रूप से लॉक को रिलीज़ करती है और अन्य थ्रेड को एनक्यू विधि में लॉक रखने की अनुमति देती है, इसलिए इसे डेडलॉक नहीं करना चाहिए। प्रतीक्षा करें फिर एक लॉक फिर से करने की कोशिश करता है। डेडलॉक जो मैंने बहुत ही कम किया था, जबकि यदि आप ऐसा करते थे तो यह लगभग हर बार होता है क्योंकि कतार में कोई डेटा होने से पहले थ्रेड शुरू होता है। – VGeorgiev

+0

हमम .. TMonitor के लिए स्रोत देख रहे हैं। अंदर, मुझे नहीं लगता कि अगर आप स्पिनकाउंट सेट नहीं करते हैं तो यह मामला है - स्पिन के लिए अधिकांश कोड छोड़ दिया जाता है जहां स्पिनकाउंट = 0 अंत में आप इसे प्राप्त करते हैं रेखा: परिणाम: = MonitorSupport.WaitOrSignalObject (शून्य, GetEvent, टाइमआउट) = WAIT_OBJECT_0; – SilverKnight

+0

मेरा मानना ​​है कि यह मामला है - हालांकि, मेरे पढ़ने से और मॉनीटर क्या समझने की कोशिश कर रहा है, यह एक विशिष्ट समय के लिए स्पिन करता है (जो बहुत छोटा होता है) - जब यह लंबा हो जाता है, तो आपके पास संभावना है डेडलॉक - स्पिनलॉक पर इस विकी आलेख पर एक नज़र डालें - http://en.wikipedia.org/wiki/Spinlock – SilverKnight

0

यह एक लंबा शॉट हो सकता है, लेकिन यहां एक और संभावना है [पूर्व उत्तर अधिक संभावना हो सकती है] (कुछ जो मैंने अभी भाग लिया था, लेकिन पहले जाना था): सिंक्रनाइज़ेशन का उपयोग डेडलॉक का कारण बन सकता है।यहाँ के बारे में क्यों ऐसा होता है एक ब्लॉग है: Delphi-Workaround-for-TThread-SynchronizeWaitFor-.aspx

लेख से उचित बिंदु:

थ्रेड एक कॉल सिंक्रनाइज़ (MethodA)

थ्रेड बी कॉल सिंक्रनाइज़ (MethodB)

फिर, मुख्य थ्रेड के संदर्भ में:

मुख्य थ्रेड संदेशों को संसाधित करते समय CheckSynchronize() को कॉल करता है

चेकसंक्रनाइज़ेशन सभी प्रतीक्षा कॉल (*) को बैच-प्रोसेस करने के लिए लागू किया गया है। इसलिए यह प्रतीक्षा कॉल (जिसमें मेथड ए और मेथड बी) शामिल है और उनके द्वारा एक-एक करके लूप उठाता है।

विधि ए मुख्य थ्रेड के संदर्भ में निष्पादित करता है। मान लें MethodA कॉल ThreadB.WaitFor

WaitFor किसी भी इंतज़ार कर कार्रवाई करने के लिए CheckSynchronize कॉल को पूरा करने के

सिद्धांत रूप में, यह तो ThreadB के सिंक्रनाइज़ (MethodB) संसाधित करना चाहिए, सिंक्रनाइज़ करने के लिए अनुमति थ्रेड बी कहते हैं। हालांकि, मेथडब पहले से ही पहले चेक सिंक्रनाइज़ कॉल का कब्जा है, इसलिए इसे कभी भी कहा जाता है।

डेडलॉक!

Embarcadero QC article अधिक विस्तार से समस्या का वर्णन करते हुए।

जबकि मुझे उपर्युक्त कोड में कोई प्रक्रिया संदेश नहीं दिखाई देता है, या उस मामले के लिए, एक प्रतीक्षाफोर जिसे सिंक्रनाइज़ के दौरान बुलाया जाएगा, यह अभी भी एक समस्या हो सकती है कि बिंदु पर एक सिंक्रनाइज़ेशन कहा जाता है, एक और धागा सिंक्रनाइज़ेशन को भी कॉल करता है - लेकिन मुख्य धागा पहले ही सिंक्रनाइज़ हो चुका है और अवरुद्ध कर रहा है।

यह मेरे साथ पहले क्लिक नहीं किया गया था, क्योंकि मैं प्लेग की तरह सिंक्रनाइज़ कॉल से बचने के लिए प्रतीत होता हूं और आम तौर पर कॉल को सिंक्रनाइज़ करने के बजाय संदेश अधिसूचना के साथ संदेश पासिंग और थ्रेड सुरक्षित सूचियों जैसे अन्य तरीकों का उपयोग करके धागे से यूआई अपडेट डिज़ाइन करता हूं।

+0

इस पर बहुत अधिक जानकारी प्राप्त करने के लिए धन्यवाद। और देरी वाले उत्तरों के लिए खेद है, मैं इन दिनों यात्रा कर रहा हूं ... आपने जो वर्णन किया है वह भी मेरे साथ हुआ और मैंने सोचा कि सिंक्रनाइज़ेशन समस्या थी। मैंने इसका इस्तेमाल किया क्योंकि ओएसएक्स पर कोई SendMessage/PostMessage नहीं है या कम से कम मुझे नहीं पता कि कोई विकल्प है या नहीं। तो उस पल में सिंक्रनाइज़ करना एक आसान समाधान था। कुछ समय पहले मैंने बहुत सारे कोड को फिर से लिखा था और मेरे पास यह फ्रीज नहीं है, लेकिन मुझे नहीं पता कि समस्या कहां थी। मैं इस्तेमाल किए गए इंडी टीसीपी घटकों से संबंधित हो सकता था, क्योंकि वे ओएसएक्स पर बहुत स्थिर नहीं थे ... – VGeorgiev

+0

कोई समस्या नहीं है। मैं कुछ और देख रहा था और इस पोस्ट में आया जिसके पास जवाब नहीं था। मैंने यह देखने के लिए एक सीखने के अभ्यास के रूप में उपयोग किया कि मॉनीटर क्लास क्या करेगा (मैंने इसका कभी भी उपयोग नहीं किया है)। मैं हमेशा अलग-अलग तकनीकों में रूचि रखता हूं जो मेरे थ्रेडेड कोड को बेहतर बना सकता है (ज्यादातर सीपीयू उपयोग को कम करने में, बल्कि विभिन्न संचार तरीकों में भी)। यह कक्षा में एक दिलचस्प गोताखोरी रहा है, और उम्मीद है कि किसी और को भी चर्चा से फायदा होगा। – SilverKnight

संबंधित मुद्दे