I was trying to write a class which let me do read and write operation on multiple files (like 5-10) while locking them from any kind of access. Everytime I access a file (doesn't matter if for read or write) a new file with the same name and a different extension is created, so other threads (belonging to different applications) are notified of the lock (ex. message.msg -> lock file message.lock created).
Every instance of the application will write in it's own file and read in all other applications files (including its).
Unfortunately, when I start several instances (like 3-4) of the application which uses this class, even if at first they look like they're working, then in a matter or seconds / maybe a couple of minutes it looks like one thread fails to release a file. This of course blocks the other threads too which are unable to read that specific file.
I say this because when everything app freezes I can see a permanent .lock file.
Of course I could put a Lock expire time (which probably would work in this scenario), but why is this happening?
To me this code looks reasonable, but of course I'm still a newbie...so...is there any mayor flaw in my ratio?
(Don't be scared by the length of this, they're only 2 functions and they do pretty much the same thing, except than for the central part)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
namespace y3kMessenger
{
static class FileLockAccess
{
public static string[] readAllLines(string path)
{
bool isLocked = false;
string[] toReturn;
string lockPath = path.Replace(Global.msgExtension, Global.lockExtension);
StreamWriter w;
//locking ...
while (!isLocked)
{
if (!File.Exists(lockPath))
{
try
{
using (w = new StreamWriter(lockPath))
{
w.WriteLine(" ");
}
isLocked = true;
}
catch (Exception e) { }
}
Thread.Sleep(10);
}
//locked, proceed with read
toReturn = File.ReadAllLines(path);
//release the lock
while (isLocked)
{
try
{
File.Delete(lockPath);
}
catch (Exception e) { }
isLocked = false;
}
return toReturn;
}
public static void writeLine(string path, string text, bool append)
{
bool isLocked = false;
string lockPath = path.Replace(Global.msgExtension, Global.lockExtension);
StreamWriter w;
//locking ...
while (!isLocked)
{
if (!File.Exists(lockPath))
{
try
{
using (w = new StreamWriter(lockPath))
{
w.WriteLine(" ");
}
isLocked = true;
}
catch (Exception e) { }
}
Thread.Sleep(10);
}
//locked, proceed with write
using (w = new StreamWriter(path, append))
w.WriteLine(text);
//release the lock
while (isLocked)
{
try
{
File.Delete(lockPath);
}
catch (Exception e) { }
isLocked = false;
}
}
}
}
EDIT: as an add to the discussion, the following code seems to work:
public static string[] readAllLines(string path)
{
bool done = false;
string[] toReturn = null;
while (!done)
{
try
{
toReturn = File.ReadAllLines(path);
done = true;
}
catch (Exception e)
{
Thread.Sleep(50);
}
}
return toReturn;
}
public static void writeLine(string path, string text, bool append)
{
bool done = false;
while (!done)
{
try
{
using (StreamWriter w = File.AppendText(path))
{
w.WriteLine(text);
}
done = true;
}
catch (Exception e)
{
Thread.Sleep(50);
}
}
}
So the problem shouldn't reside in what threads are doing (I haven't changed anything else since the interface exposed by these methods is the same as the first 2)
Related
I've created a service, which "locks" the desktop background, so you can't change it. In general, the service compares the current wallpaper with the one I want to be the new wallpaper. If it's not the same, it gets overwritten. This is my OnTimer()-Method, which gets executed every 2 seconds:
private void OnTimer(object sender, ElapsedEventArgs e)
{
if (!File.Exists("C:/Program Files/image.jpg"))
{
Assembly myAssembly = Assembly.GetExecutingAssembly();
Stream s = myAssembly.GetManifestResourceStream("MsService.Gandalf.jpg"); ;
byte[] b;
using (BinaryReader br = new BinaryReader(s))
{
b = br.ReadBytes((int)s.Length);
}
while (true)
{
try
{
File.WriteAllBytes("C:/Program Files/image.jpg", b);
break;
}
catch (Exception)
{
//stuff
}
}
}
//{path} is %Appdata%
if (!FileEquals($"{path}\\Microsoft\\Windows\\Themes\\TranscodedWallpaper", "C:/Program Files/image.jpg"))
{
byte[] file = File.ReadAllBytes("C:/Program Files/image.jpg");
while (true)
{
try
{
File.WriteAllBytes($"{path}\\Microsoft\\Windows\\Themes\\TranscodedWallpaper", file);
break;
}
catch (Exception)
{
//stuff
}
}
}
}
That's the FileEquals Method:
static bool FileEquals(string path1, string path2)
{
byte[] file1 = File.ReadAllBytes(path1);
byte[] file2 = File.ReadAllBytes(path2);
if (file1.Length == file2.Length)
{
for (int i = 0; i < file1.Length; i++)
{
if (file1[i] != file2[i])
{
return false;
}
}
return true;
}
return false;
}
When I start the service, It only changes the wallpaper if I change it actively, even if it already is another one that the one I want it to be, and only once. The service outputs no error when debugging, and the service doesn't crash. Also when I change the wallpaper, the breakpoints in the if(!File.Equals(...)) gets triggered. Virus scanner isn't alerting anything too. Why doesn't it work anyway?
I wrote the simplified version of my program below. Process A launches a child process (Process B). I use an anonymous pipe to write information about the progress of a method running on process B. Meanwhile I have a function in process A that continually reads from a stream to see if there is a new update coming in from the pipe. If there is, the form on process A is updated to reflect the progress. This works as expected, however I am wondering if there is a better way to accomplish this without having to continually check the stream to see if there are any new updates to the progress.
/////////////////
///Process A ////
/////////////////
public void LaunchProcessB()
{
using (AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream(PipeDirection.In,
HandleInheritability.Inheritable))
{
var _Process = new Process();
_Process.StartInfo.FileName = exeString;
_Process.StartInfo.Arguments = pipeServer.GetClientHandleAsString()
_Process.StartInfo.RedirectStandardOutput = true;
_Process.StartInfo.RedirectStandardInput = true;
_Process.StartInfo.CreateNoWindow = true;
_Process.StartInfo.UseShellExecute = false;
_Process.Start(); //launches process B
pipeServer.DisposeLocalCopyOfClientHandle();
using (StreamReader sr = new StreamReader(pipeServer))
{
try
{
while (true)
{
string temp = sr.ReadLine();
if (temp == null) break;
int result;
if (Int32.TryParse(temp, out result))
ShowDocumentProgress(result);
else ShowProgress(temp);
}
}
catch (Exception)
{
//error occured when reading from stream.
}
}
if (!_Process.Responding && !_Process.HasExited)
{
_Process.Kill();
return;
}
_Process.WaitForExit(10000);
}
}
private void ShowProgressPercent(int percentage)
{
if (percentage > currentPercentage)
{
progressBar.Value = percentage;
}
}
private void ShowProgress(string progressString)
{
labelMessage.Text = progressString;
}
/////////////////
///Process B ////
/////////////////
private StreamWriter _progressWriter;
private PipeStream _progressPipe;
static int Main(string[] args)
{
using (progressPipe = new AnonymousPipeClientStream(PipeDirection.Out, args[0]))
using (_progressWriter = new StreamWriter(_progressPipe))
{
RunLongProcess()
}
}
private void RunLongProcess()
{
//attaches events to PercentProgress and StageProgress methods.
}
private void PercentProgress(int percentage)
{
_progressWriter.WriteLine(percentage.ToString());
_progressPipe.WaitForPipeDrain();
}
private void StageProgress(string stage)
{
_progressWriter.WriteLine(stage);
_progressPipe.WaitForPipeDrain();
}
The while condition is not necessary. Simply read until temp is null. That's the end signal of the stream.
Make this a while(true) loop.
I think you also need to add exception handling to catch the process terminating and severing the pipe. !_Process.HasExited && pipeServer.IsConnected is not enough because it might be true but immediately switch to false after the test.
I also would add a WaitForExit at the end to make sure the system is quiesced before you continue.
This is my code
using UnityEngine;
using System.Collections;
using System;
using System.IO;
using System.Net.Sockets;
public class s_TCP : MonoBehaviour {
internal Boolean socketReady = false;
TcpClient mySocket;
NetworkStream theStream;
StreamWriter theWriter;
StreamReader theReader;
String Host = "198.57.44.231";
Int32 Port = 1337;
string channel = "testingSona";
void Start () {
setupSocket();
//string msg = "__SUBSCRIBE__"+channel+"__ENDSUBSCRIBE__";
string msg = "Sending By Sona";
writeSocket(msg);
readSocket();
}
void Update () {
//readSocket();
}
public void setupSocket() {
try {
mySocket = new TcpClient(Host, Port);
theStream = mySocket.GetStream();
theWriter = new StreamWriter(theStream);
theReader = new StreamReader(theStream);
socketReady = true;
}
catch (Exception e) {
Debug.Log("Socket error: " + e);
}
}
public void writeSocket(string theLine) {
if (!socketReady)
return;
String foo = theLine + "\r\n";
theWriter.Write(foo);
theWriter.Flush();
}
public String readSocket() {
if (!socketReady)
return "";
if (theStream.DataAvailable){
string message = theReader.ReadLine();
print(message);print(12345);
return theReader.ReadLine();
}
else{print("no value");
return "";
}
}
public void closeSocket() {
if (!socketReady)
return;
theWriter.Close();
theReader.Close();
mySocket.Close();
socketReady = false;
}
}
Connection created. But message not writing into server and reading
How can i do it
I think you have taken this code from http://answers.unity3d.com/questions/15422/unity-project-and-3rd-party-apps.html, but I think there is an error in this code. I'll repeat here what I posted there.
The following code does not work correctly:
public String readSocket() {
if (!socketReady)
return "";
if (theStream.DataAvailable)
return theReader.ReadLine();
return "";
}
This caused me a headache for quite few hours. I think that checking DataAvailable on the stream is not a reliable way to check if there is data to be read on the streamreader. So you do not want to check for DataAvailable. However, if you just remove that, then the code will block on ReadLine when there is no more to read. So instead, you need to set a timeout for reading from the stream, so that you won't wait longer than (say) a millisecond:
theStream.ReadTimeout = 1;
And then, you can use something like:
public String readSocket() {
if (!socketReady)
return "";
try {
return theReader.ReadLine();
} catch (Exception e) {
return "";
}
}
This code isn't perfect, I still need to improve it (e.g., check what kind of exception was raised, and deal with it appropriately). And maybe there's a better way overall to do this (I experimented with using Peek(), but the -1 it returns I suspect is for when the socket closes, and not just when there is no more data to read for now). However, this should solve problems with the posted code, like those I was having. If you're finding data is missing from the server, then it's probably sitting in your reader stream, and won't be read until new data is sent from the server and stored in the stream such that theStream.DataAvailable returns true.
I was reading this article (Can't delete a file using threads) about my problem but things are getting difficult to me.
My problem is really simple, I just want to delete this old file, if I start the method "dlMoveNovaVersao" normally the file is deleted but if I put this on a thread (like bellow) I got "You are not allow". Someone knows what's the problem? (I wanna use thread).
private void verificaVersaoSupervisor_Tick(object sender, EventArgs e)
{
Thread threadConexao = new Thread(threadVerificaConexao);
threadConexao.Start();
}
public void threadVerificaConexao()
{
try
{
Dns.GetHostEntry("www.google.com.br");
if (verificaVersao())
{
try
{
verificaKillSupervisor();
dlMoveNovaVersao();
Application.Exit();
}
catch (Exception)
{ }
}
else
{
Application.Exit();
}
}
catch (Exception)
{ }
}
public void dlMoveNovaVersao()
{
WebClient webClient = new WebClient();
webClient.DownloadFile("Anywebsite", #"c:\temp\supervisor.exe);
try
{
File.Delete(#"c:\Test\supervisor.exe); //This file is always there!
}
catch (Exception err)
{
MessageBox.Show(err.Message);
}
Just discribe the purpose, My program (Supervisor Starter) check on website if I have an old version of "Supervisor" running (using XML), If it's true my "Supervisor Starter" verify if there is a process called "Supervisor" running and kill it after that "Supervisor Starter" download the new version and run it. (The program is small and the update don't take more then 4 seconds).
The problem start when my "Supervisor Starter" try delete the old version of my program. If I use thread I receive "I haven't permission to access the file", if I use the same method on Form class the file is deleted.
I suspect that you're running the thread while the file is in use. When the thread runs, it runs in parallel with the current thread. Have you ensured that that file is closed?.
Otherwise I think that the thread maybe being created with a credentials that are not yours. But I'm pretty sure this is not the case.
See if this is different for each case
catch (Exception err)
{
MessageBox.Show("User {0}. Message {1}",
System.Security.Principal.WindowsIdentity.GetCurrent().Name,
err.Message);
}
This is my functions for deleting files in threads if the files are in used
private static void Delete(System.IO.FileInfo file)
{
if (file.Exists)
{
int Attempt = 0;
bool ShouldStop = false;
while (!ShouldStop)
{
if (CanDelete(file))
{
file.Delete();
ShouldStop = true;
}
else if (Attempt >= 3)
{
ShouldStop = true;
}
else
{
// wait one sec
System.Threading.Thread.Sleep(1000);
}
Attempt++;
}
}
}
private static bool CanDelete(System.IO.FileInfo file)
{
try
{
//Just opening the file as open/create
using (FileStream fs = new FileStream(file.FullName, FileMode.OpenOrCreate))
{
//If required we can check for read/write by using fs.CanRead or fs.CanWrite
}
return false;
}
catch (IOException ex)
{
//check if message is for a File IO
string __message = ex.Message.ToString();
if (__message.Contains("The process cannot access the file"))
return true;
else
throw;
}
}
For my network based project I need to lock some codes to prevent simultaneous access.
This is my code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Utility;
using DataBaseConnection;
using System.Net.Sockets;
using System.Data;
using System.IO;
namespace SunHavenClasses
{
public delegate void CtatReceiveDelegate(string message);
public class ServerHandlerClass
{
public event CtatReceiveDelegate OnChatDataReceive;
private Settings settings;
private DBCon con;
private Utility.Network.Server server;
private Dictionary<string, Socket> UsersOnline;
private Dictionary<string, int> unAuthenticatedIps;
private string pass = "logisoftlogicielbarundipankar";
public ServerHandlerClass(Settings s)
{
settings = s;
con = s.GetConnection();
server = new Utility.Network.Server(7777);
server.ClientConnectEventArise += OnUserConnect;//.OnClientConnect(OnUserConnect);
server.ClientDataReceiveEventArise += OnUsersDataReceive;
server.ClientDataSendEventArise += OnDataSendToUser;
server.ClientDisconnectEventArise += OnUserDisconnect;
server.OnBlockUser += OnUserBlocked;
UsersOnline = new Dictionary<string, Socket>();
unAuthenticatedIps = new Dictionary<string, int>();
}
private void OnUserConnect(Utility.Network.ServerEventArguments e)
{
Stream data = Utility.Serializing.Serialize(settings);
data = ZipNEncrypt.Zip(new string[] { "settings" }, new Stream[] { data }, pass);
server.Send(data, e.ClientSocket);
//MessageBox.Show(e.ClientSocket.RemoteEndPoint.ToString() + " is Connected!!");
}
private void OnUsersDataReceive(Utility.Network.ServerEventArguments e)
{
Dictionary<string, System.IO.Stream> data = ZipNEncrypt.Unzip(e.Data, pass);
User user;
try
{
user = (User)Serializing.Deserialize(data["user"]);
if (!UsersOnline.ContainsKey(user.GetUserId()))
{
server.BlockIp(e.ClientSocket);
return;
}
data.Remove("user");
}
catch (Exception)
{
bool passed = true;
foreach (string key in data.Keys)
{
if (key.Equals("LoggedIn")) break;
string[] str = key.Split('_');
if (str[0].Equals("GetData"))
{
string strr = (string)Serializing.Deserialize(data[key]);
if (strr.Contains("Users"))
{
string ip = e.ClientSocket.RemoteEndPoint.ToString().Split(':')[0];
/*CHANGE 1.2.10 00:14*/
lock (unAuthenticatedIps)
{
if (!unAuthenticatedIps.ContainsKey(ip))
{
unAuthenticatedIps.Add(ip, 1);
}
else unAuthenticatedIps[ip] += 1;
if (unAuthenticatedIps[ip] >= 11) passed = false;
}
/*CHANGE 1.2.10 00:14*/
break;
}
else passed = false;//server.AddBlockedIp(ip);
}
else passed = false;
}
if (!passed)
{
server.BlockIp(e.ClientSocket);
}
}
foreach (string key in data.Keys)
{
if (key.Equals("LoggedIn"))
{
try
{
User u = (User)Serializing.Deserialize(data["LoggedIn"]);
if (!UsersOnline.ContainsKey(u.GetUserId()))
{
if (User.ValidateUser(u.GetUserId(), u.GetPassword(), con))
{
/*CHANGE 1.2.10 00:14*/
lock (UsersOnline)
{
UsersOnline.Add(u.GetUserId(), e.ClientSocket);
string ip = e.ClientSocket.RemoteEndPoint.ToString().Split(':')[0];
Utility.Log.Write("UserLog.log", u.GetUserId() +
" Logged In From Ip " + ip);
}
/*CHANGE 1.2.10 00:14*/
}
else
{
server.BlockIp(e.ClientSocket);
return;
}
}
else
{
Stream tmpStream = Serializing.Serialize("Same User");
tmpStream = ZipNEncrypt.Zip(new string[] { key + "ERROR_SameUser" },
new Stream[] { tmpStream }, pass);
server.Send(tmpStream, e.ClientSocket);
return;
}
}
catch (Exception) { }
return;
}
else if (key.Equals("chat"))
{
string ip = e.ClientSocket.RemoteEndPoint.ToString().Split(':')[0];
string message = ip + " : "+ (string)Serializing.Deserialize(data[key]);
OnChatDataReceive(message);
return;
}
string[] str = key.Split('_');
Stream dataStream = null;
object obj = null;
try
{
if (str[0].StartsWith("Get"))
{
if (str[0].Equals("GetData"))
{
string query = (string)Serializing.Deserialize(data[key]);
obj = con.GetData(query);
}
else if (str[0].Equals("GetColumn"))
{
string query = (string)Serializing.Deserialize(data[key]);
string[] tmp = query.Split('%');
obj = con.GetColumn(tmp[0], tmp[1]);
}
else if (str[0].Equals("GetColumnDistrinctValue"))
{
string query = (string)Serializing.Deserialize(data[key]);
string[] tmp = query.Split('%');
obj = con.GetColumnDistrinctValue(tmp[0], tmp[1]);
}
}
else
{
lock (this)
{
if (str[0].Equals("ExecuteUpdate"))
{
if (str[1].Equals("Query"))
{
Query query = (Query)Serializing.Deserialize(data[key]);
obj = con.ExecuteUpdate(query);
}
else if (str[1].Equals("String"))
{
string query = (string)Serializing.Deserialize(data[key]);
obj = con.ExecuteUpdate(query);
}
}
else if (str[0].Equals("ExecuteBatchUpdate"))
{
if (str[1].Equals("Query"))
{
Query[] query = (Query[])Serializing.Deserialize(data[key]);
obj = con.ExecuteBatchUpdate(query);
}
else if (str[1].Equals("String"))
{
string[] query = (string[])Serializing.Deserialize(data[key]);
obj = con.ExecuteBatchUpdate(query);
}
}
else if (str[0].Equals("ExecutrInsert"))
{
Query query = (Query)Serializing.Deserialize(data[key]);
obj = con.ExecutrInsert(query);
}
}
}
dataStream = Serializing.Serialize(obj);
dataStream = ZipNEncrypt.Zip(new string[] { key },
new Stream[] { dataStream }, pass);
}
catch (Exception ex)
{
dataStream = Serializing.Serialize(ex.Message);
dataStream = ZipNEncrypt.Zip(new string[] { key + "_ERROR" },
new Stream[] { dataStream }, pass);
}
server.Send(dataStream, e.ClientSocket);
}
}
private void OnDataSendToUser(Utility.Network.ServerEventArguments e)
{
}
private void OnUserDisconnect(Utility.Network.ServerEventArguments e)
{
//System.Windows.Forms.MessageBox.Show("Disconnected");
string ip = e.ClientSocket.RemoteEndPoint.ToString().Split(':')[0];
/*CHANGE 1.2.10 00:14*/
lock (unAuthenticatedIps)
{
if (unAuthenticatedIps.ContainsKey(ip))
unAuthenticatedIps.Remove(ip);
}
lock (UsersOnline)
{
foreach (string key in UsersOnline.Keys)
if (UsersOnline[key].Equals(e.ClientSocket))
{
Utility.Log.Write("UserLog.log", key + " Logged Out From Ip " + ip);
UsersOnline.Remove(key);
break;
}
}
/*CHANGE 1.2.10 00:14*/
}
private void OnUserBlocked(Utility.Network.ServerEventArguments e)
{
string ip = e.ClientSocket.RemoteEndPoint.ToString().Split(':')[0];
Utility.Log.Write("UserLog.log", "Blocked For Illegal Access From Ip " + ip);
}
public void Send(Stream dataStream)
{
foreach (string key in UsersOnline.Keys)
{
try
{
server.Send(dataStream, UsersOnline[key]);
}
catch (Exception) { }
}
}
public void Send(Stream dataStream, Socket client)
{
try
{
server.Send(dataStream, client);
}
catch (Exception) { }
}
/*changed*/
public bool AddUser(string userId, Socket socket)
{
if (UsersOnline.ContainsKey(userId)) return false;
UsersOnline.Add(userId, null);
return true;
}
public void RemoveUser(string userId)
{
if (!UsersOnline.ContainsKey(userId) || UsersOnline[userId] != null) return;
UsersOnline.Remove(userId);
}
}
}
Now I am not sure that I am using lock correctly.Please Give me some advice.
Thanks.
I'm guessing you read a lot more than you write? If so, a ReaderWriterLockSlim may be more appropriate to reduce blocking (take read when you just want to check for the key, and write to manipulate the data).
By that I mean you could do double-checked locking with a read at first, then if it fails take a write lock, check again and add if necessary.
Also - the lock(this) is generally frowned upon; having a separate lock object is preferred.
Note that to be effective, all access must respect the lock; there are some places where UsersOnline is locked, and some places where it is accessed without a lock, for example; those second cases may explode in a gooey mess.
For example:
if (!UsersOnline.ContainsKey(u.GetUserId()))
{
if (User.ValidateUser(u.GetUserId(), u.GetPassword(), con))
{
/*CHANGE 1.2.10 00:14*/
lock (UsersOnline)
{
UsersOnline.Add(u.GetUserId(), e.ClientSocket);
In the above, if it is possible that two threads are looking at UsersOnline, then you've already failed by attempting ContainsKey without the lock. If another thread is mutating the state when you do this.... boom.
First of all, you code isn't thread safe at all. In your code you locks only modifying operations (Remove, Add), but also you should lock all access to shared fields. Actually this code not thread safe at all. I think that in this case ReaderWriterLockSlim - would be the best choise.
Second. lock(this) - very bed idea. You should use special objects for this.
Finally, I think your code is messy and hard to understand. Maybe your class solve many different tasks. Maybe you should extract some logic to separate classes (for example create guarded dictionaries as separate classes) or something else.
Sample of using ReaderWriterLockSlim:
someSharedResource;
someSharedResourceRWLock = new ReaderWriterLockSlim();
Some reading code:
try
{
someSharedResourceRWLock.EnterReadLock();
//access to someSharedResource for reading
}
finally
{
someSharedResourceRWLock.ExitReadLock();
}
Some writing code:
try
{
someSharedResourceRWLock.EnterWriteLock();
//access to someSharedResource for modifications
}
finally
{
someSharedResourceRWLock.ExitWriteLock();
}
You are using it correctly some of the time. unAuthenticatedIps is being protected correctly, but UsersOnline is not. Let's consider two parallel threads passing through your code:
Thread A Thread B
-------- --------
if (!UsersOnline.ContainsKey(u.GetUserId()) Statement's true Statement's true
{
lock (UsersOnline) Get lock Block
{
UsersOnline.Add UsersOnline.Add
} Release Lock
} Get lock
UsersOnline.Add
Release lock
Notice that both threads A and B modify the UsersOnline dictionary. This structure would properly protect that object:
if (!UsersOnline.ContainsKey(u.GetUserId()))
{
if (User.ValidateUser(u.GetUserId(), u.GetPassword(), con))
{
lock (UsersOnline)
{
// Note the additional check in case another thread
// added this already
if (!UsersOnline.ContainsKey(u.GetUserId())
{
UsersOnline.Add(u.GetUserId(), e.ClientSocket);
// ...
}
}
}
else
{
server.BlockIp(e.ClientSocket);
return;
}
}
As far as the last lock goes (lock (this)), I don't yet see why you would need it. str and obj are both local variables, so you shouldn't need to worry about them being modified by separate threads. And, as other have stated, locking this is not recommended.
The advice given so far has been great, but I have a design suggestion you may want to consider. Replace this:
private Dictionary<string, Socket> UsersOnline;
with a custom Thread Safe Dictionary
private ThreadSafeDictionary<string, Socket> UsersOnline
If its a fit for your needs, it would be a nice way to separate the business logic from the threading logic.