Besides the “honor code”, is there a difference in the purpose of the highlighted “lock object” and the data lock directly?

I have two streams: one that transfers updates, and one that writes them to disk. Only the latest update matters, so I don't need a PC queue.

In a nutshell:

  • The feeder thread passes the latest update to the buffer, and then sets a flag to indicate a new update.
  • The write stream checks the flag, and if it indicates new content, writes a buffered update to disk and turns off the flag again.

I am currently using a dedicated lock object to make sure there is no inconsistency, and I wonder what the differences are with flag and buffer locks directly . The only one I know about is that the highlighted lock object requires trust that anyone who wants to manipulate the flag and buffer uses the lock.

Relevant Code:

private object cacheStateLock = new object(); string textboxContents; bool hasNewContents; private void MainTextbox_TextChanged(object sender, TextChangedEventArgs e) { lock (cacheStateLock) { textboxContents = MainTextbox.Text; hasNewContents = true; } } private void WriteCache() // running continually in a thread { string toWrite; while (true) { lock (cacheStateLock) { if (!hasNewContents) continue; toWrite = textboxContents; hasNewContents = false; } File.WriteAllText(cacheFilePath, toWrite); } } 
+6
source share
1 answer

First of all, if you are trying to use the bool flag this way, you should mark it as volatile ( which is not recommended at all , but better than your code).

The second thing to note is that the lock statement is a sintax sugar for the Monitor methods of the class, so even if you were able to provide a value type for it (which, by the way, is a compilation error), two different threads will get their own version of the flag, making lock useless. Therefore, you must specify a reference type for the lock statement .

Thirdly, strings are immutable in C# , so it is theoretically possible for some method to keep the old string reference and execute lock in the wrong order. Alternatively, the string may become null from MainTextbox.Text in your case, which will run at run time, comparing to a private object that will never change (you should mark it as readonly way).

So, introducing a dedicated object for synchronization is the easiest and most natural way to separate the lock from the actual logic .

As for your source code, it has a problem since MainTextbox_TextChanged can override text that was not written. You can enter additional synchronization logic or use some library here. @Aron suggested Rx here, I personally prefer TPL Dataflow , it doesn't matter.

You can add the BroadcastBlock associated with the ActionBlock<string>(WriteCache) , which will remove the endless loop from the WriteCache and lock method from both of your methods:

 var broadcast = new BroadcastBlock<string>(s => s); var consumer = new ActionBlock<string>(s => WriteCache(s)); broadcast.LinkTo(consumer); // fire and forget private async void MainTextbox_TextChanged(object sender, TextChangedEventArgs e) { await broadcast.SendAsync(MainTextbox.Text); } // running continually in a thread without a loop private void WriteCache(string toWrite) { File.WriteAllText(cacheFilePath, toWrite); } 
0
source

Source: https://habr.com/ru/post/1013895/


All Articles