From 7e6ae3704fbad91bac8e4098f74c0eb2be5c3df7 Mon Sep 17 00:00:00 2001 From: kexogg Date: Thu, 30 Jan 2025 23:39:01 +0500 Subject: [PATCH 1/3] homework --- .../ReaderWriterLock/ReaderWriterLock.csproj | 3 +- .../ReaderWriterLock/SharedReourceTests.cs | 34 +++++++----- .../ReaderWriterLock/SharedResourceBase.cs | 1 + .../ReaderWriterLock/SharedResourceLock.cs | 19 +++++-- .../SharedResourcePerfomanceTests.cs | 52 ++++++++++++++----- .../ReaderWriterLock/SharedResourceRwLock.cs | 33 ++++++++++-- 6 files changed, 109 insertions(+), 33 deletions(-) diff --git a/homework 1/ReaderWriterLock/ReaderWriterLock.csproj b/homework 1/ReaderWriterLock/ReaderWriterLock.csproj index a5bff8a..2ba6f23 100644 --- a/homework 1/ReaderWriterLock/ReaderWriterLock.csproj +++ b/homework 1/ReaderWriterLock/ReaderWriterLock.csproj @@ -2,10 +2,11 @@ Exe - net8.0 + net9.0 + diff --git a/homework 1/ReaderWriterLock/SharedReourceTests.cs b/homework 1/ReaderWriterLock/SharedReourceTests.cs index 0fd91d2..d75840d 100644 --- a/homework 1/ReaderWriterLock/SharedReourceTests.cs +++ b/homework 1/ReaderWriterLock/SharedReourceTests.cs @@ -1,11 +1,9 @@ -using System; using System.Linq; using System.Threading; +using FluentAssertions; using NUnit.Framework; -using NUnit.Framework.Legacy; namespace ReaderWriterLock; - [TestFixture] public class SharedResourceTests { @@ -16,20 +14,32 @@ public class SharedResourceTests [Test] public void TestConcurrentReadWrite() { - // Реализовать проверку конкурентной записи и чтения, где в конце должны проверить что данные последнего потока записаны - // Проверка должна быть многопоточной. - // Потоков чтения должно быть ReadersThreads, потоков записи должно быть WritersThreads - - ClassicAssert.AreEqual($"Data {WritersThreads-1}", _sharedResource.Read()); + _sharedResource = new SharedResourceLock(); + TestSharedResource(); } [Test] public void TestConcurrentReadWriteRwLock() { - // Реализовать проверку конкурентной записи и чтения, где в конце должны проверить что данные последнего потока записаны - // Проверка должна быть многопоточной - // Потоков чтения должно быть ReadersThreads, потоков записи должно быть WritersThreads + _sharedResource = new SharedResourceRwLock(); + TestSharedResource(); + } + + private void TestSharedResource() + { + var threads = Enumerable + .Range(0, WritersThreads) + .Select(x => new Thread(() => _sharedResource.Write(x.ToString()))) + .Concat( + Enumerable + .Range(0, ReadersThreads) + .Select(_ => new Thread(() => _sharedResource.Read()))) + .ToArray(); - ClassicAssert.AreEqual($"Data {WritersThreads-1}", _sharedResource.Read()); + threads.ForEach(t => t.Start()); + threads.ForEach(t => t.Join()); + + var expected = (WritersThreads - 1).ToString(); + _sharedResource.Read()[^expected.Length..].Should().BeEquivalentTo(expected); } } \ No newline at end of file diff --git a/homework 1/ReaderWriterLock/SharedResourceBase.cs b/homework 1/ReaderWriterLock/SharedResourceBase.cs index b7c112d..ef38b15 100644 --- a/homework 1/ReaderWriterLock/SharedResourceBase.cs +++ b/homework 1/ReaderWriterLock/SharedResourceBase.cs @@ -5,6 +5,7 @@ public abstract class SharedResourceBase public abstract void Write(string data); public abstract string Read(); public abstract long ComputeFactorial(int number); + protected string SharedResource = string.Empty; protected long Factorial(int number) { diff --git a/homework 1/ReaderWriterLock/SharedResourceLock.cs b/homework 1/ReaderWriterLock/SharedResourceLock.cs index 03dd0ee..6fd11f0 100644 --- a/homework 1/ReaderWriterLock/SharedResourceLock.cs +++ b/homework 1/ReaderWriterLock/SharedResourceLock.cs @@ -1,19 +1,32 @@ +using System.Threading; + namespace ReaderWriterLock; public class SharedResourceLock : SharedResourceBase { + private readonly Lock _lock = new(); + public override void Write(string data) { - throw new System.NotImplementedException(); + lock (_lock) + { + SharedResource = data; + } } public override string Read() { - throw new System.NotImplementedException(); + lock (_lock) + { + return SharedResource; + } } public override long ComputeFactorial(int number) { - throw new System.NotImplementedException(); + lock (_lock) + { + return Factorial(number); + } } } \ No newline at end of file diff --git a/homework 1/ReaderWriterLock/SharedResourcePerfomanceTests.cs b/homework 1/ReaderWriterLock/SharedResourcePerfomanceTests.cs index a801ba8..1c2881b 100644 --- a/homework 1/ReaderWriterLock/SharedResourcePerfomanceTests.cs +++ b/homework 1/ReaderWriterLock/SharedResourcePerfomanceTests.cs @@ -1,9 +1,9 @@ using System; -using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Threading; +using FluentAssertions; using NUnit.Framework; -using NUnit.Framework.Legacy; namespace ReaderWriterLock; @@ -13,31 +13,55 @@ public class SharedResourcePerformanceTests private SharedResourceBase _sharedResource; private const int WritersThreads = 100; private const int ReadersThreads = 1000; - private const int NumberOfIterations = 10000; - private const int FactorialNumber = 60; // Большое число для вычисления факториала + private const int NumberOfIterations = 100; + private const int FactorialNumber = 60; [Test] public void TestLockPerformance() { _sharedResource = new SharedResourceLock(); - long lockTime = MeasurePerformance(); + var lockTime = MeasurePerformance(); Console.WriteLine($"Lock time taken: {lockTime} ms"); _sharedResource = new SharedResourceRwLock(); - long rwLockTime = MeasurePerformance(); + var rwLockTime = MeasurePerformance(); Console.WriteLine($"ReaderWriterLock time taken: {rwLockTime} ms"); - // Проверка, что время выполнения с ReaderWriterLock меньше, чем с Lock - ClassicAssert.Less(rwLockTime, lockTime, "ReaderWriterLock should be faster than Lock"); + rwLockTime.Should().BeLessThan(lockTime, "ReaderWriterLock should be faster than Lock"); } private long MeasurePerformance() { - // Нужно реализовать тест производительности. - // В многопоточном режиме нужно запустить: - // - Чтение общего ресурса в количестве ReadersThreads читающих потоков - // - Запись значений в количестве WritersThreads записывающих потоков - // - В вызовах читателей и писателей обязательно нужно вызывать подсчет факториала для симуляции полезной нагрузки - throw new NotImplementedException(); + var threads = Enumerable + .Range(0, ReadersThreads) + .Select(_ => new Thread(Read)) + .Concat(Enumerable.Range(0, WritersThreads).Select(_ => new Thread(Write))) + .ToArray(); + + var sw = Stopwatch.StartNew(); + threads.ForEach(t => t.Start()); + threads.ForEach(t => t.Join()); + sw.Stop(); + + return sw.ElapsedMilliseconds; + } + + + private void Write() + { + for (var i = 0; i < NumberOfIterations; i++) + { + _sharedResource.Write(i.ToString()); + _sharedResource.ComputeFactorial(FactorialNumber); + } + } + + private void Read() + { + for (var i = 0; i < NumberOfIterations; i++) + { + _sharedResource.Read(); + _sharedResource.ComputeFactorial(FactorialNumber); + } } } \ No newline at end of file diff --git a/homework 1/ReaderWriterLock/SharedResourceRwLock.cs b/homework 1/ReaderWriterLock/SharedResourceRwLock.cs index 6bb8854..4c08430 100644 --- a/homework 1/ReaderWriterLock/SharedResourceRwLock.cs +++ b/homework 1/ReaderWriterLock/SharedResourceRwLock.cs @@ -1,19 +1,46 @@ +using System.Threading; + namespace ReaderWriterLock; public class SharedResourceRwLock : SharedResourceBase { + private readonly ReaderWriterLockSlim _readerWriterLock = new(); public override void Write(string data) { - throw new System.NotImplementedException(); + _readerWriterLock.EnterWriteLock(); + try + { + SharedResource = data; + } + finally + { + _readerWriterLock.ExitWriteLock(); + } } public override string Read() { - throw new System.NotImplementedException(); + _readerWriterLock.EnterReadLock(); + try + { + return SharedResource; + } + finally + { + _readerWriterLock.ExitReadLock(); + } } public override long ComputeFactorial(int number) { - throw new System.NotImplementedException(); + _readerWriterLock.EnterReadLock(); + try + { + return Factorial(number); + } + finally + { + _readerWriterLock.ExitReadLock(); + } } } \ No newline at end of file From d564279465c7399ec0512eba9e0d78943bc9bece Mon Sep 17 00:00:00 2001 From: kexogg Date: Fri, 31 Jan 2025 00:56:43 +0500 Subject: [PATCH 2/3] Increase the number of iterations in SharedResourcePerformanceTests --- homework 1/ReaderWriterLock/SharedResourcePerfomanceTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homework 1/ReaderWriterLock/SharedResourcePerfomanceTests.cs b/homework 1/ReaderWriterLock/SharedResourcePerfomanceTests.cs index 1c2881b..0b6ab55 100644 --- a/homework 1/ReaderWriterLock/SharedResourcePerfomanceTests.cs +++ b/homework 1/ReaderWriterLock/SharedResourcePerfomanceTests.cs @@ -13,7 +13,7 @@ public class SharedResourcePerformanceTests private SharedResourceBase _sharedResource; private const int WritersThreads = 100; private const int ReadersThreads = 1000; - private const int NumberOfIterations = 100; + private const int NumberOfIterations = 1000; private const int FactorialNumber = 60; [Test] @@ -22,7 +22,7 @@ public void TestLockPerformance() _sharedResource = new SharedResourceLock(); var lockTime = MeasurePerformance(); Console.WriteLine($"Lock time taken: {lockTime} ms"); - + _sharedResource = new SharedResourceRwLock(); var rwLockTime = MeasurePerformance(); Console.WriteLine($"ReaderWriterLock time taken: {rwLockTime} ms"); From 1024bf84b39eb247178b5eb4d928a366fc25d428 Mon Sep 17 00:00:00 2001 From: kexogg Date: Thu, 6 Feb 2025 15:58:30 +0500 Subject: [PATCH 3/3] homework 2 --- .../Clients/ClusterClientBase.cs | 13 ++++++ .../Clients/ParallelClusterClient.cs | 44 ++++++++++++++----- .../Clients/RandomClusterClient.cs | 8 ++-- .../Clients/RoundRobinClusterClient.cs | 39 +++++++++++----- .../Clients/SmartClusterClient.cs | 39 +++++++++++++--- homework 2/ClusterClient/ClusterClient.csproj | 2 +- homework 2/ClusterClient/Models/Instance.cs | 3 ++ homework 2/ClusterServer/ClusterServer.csproj | 2 +- homework 2/ClusterServer/Program.cs | 10 ++--- homework 2/ClusterTests/ClusterTests.csproj | 2 +- 10 files changed, 122 insertions(+), 40 deletions(-) create mode 100644 homework 2/ClusterClient/Models/Instance.cs diff --git a/homework 2/ClusterClient/Clients/ClusterClientBase.cs b/homework 2/ClusterClient/Clients/ClusterClientBase.cs index 23a2ffd..49a2d80 100644 --- a/homework 2/ClusterClient/Clients/ClusterClientBase.cs +++ b/homework 2/ClusterClient/Clients/ClusterClientBase.cs @@ -40,5 +40,18 @@ protected async Task ProcessRequestAsync(WebRequest request) return result; } } + + protected async Task TryProcessRequestAsync(WebRequest request) + { + try + { + return await ProcessRequestAsync(request); + } + catch (WebException ex) + { + Log.ErrorFormat("Request to {0} failed: {1}", request.RequestUri, ex.Message); + } + return null; + } } } \ No newline at end of file diff --git a/homework 2/ClusterClient/Clients/ParallelClusterClient.cs b/homework 2/ClusterClient/Clients/ParallelClusterClient.cs index 5531800..6595033 100644 --- a/homework 2/ClusterClient/Clients/ParallelClusterClient.cs +++ b/homework 2/ClusterClient/Clients/ParallelClusterClient.cs @@ -1,23 +1,45 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using log4net; -namespace ClusterClient.Clients +namespace ClusterClient.Clients; + +public class ParallelClusterClient : ClusterClientBase { - public class ParallelClusterClient : ClusterClientBase + protected override ILog Log => LogManager.GetLogger(typeof(ParallelClusterClient)); + + public ParallelClusterClient(string[] replicaAddresses) : base(replicaAddresses) { - public ParallelClusterClient(string[] replicaAddresses) : base(replicaAddresses) - { - } + } - public override Task ProcessRequestAsync(string query, TimeSpan timeout) + public override async Task ProcessRequestAsync(string query, TimeSpan timeout) + { + var tasks = ReplicaAddresses + .Select(baseUri => CreateRequest(baseUri + "?query=" + query)) + .Select(TryProcessRequestAsync) + .ToList(); + var delay = Task.Delay(timeout); + + while (tasks.Count != 0) { - throw new NotImplementedException(); + var task = Task.WhenAny(tasks); + + await Task.WhenAny(task, delay); + + if (delay.IsCompleted) + { + throw new TimeoutException(); + } + + if (task.Result.Result != null) + { + return task.Result.Result; + } + + tasks.Remove(task.Result); } - protected override ILog Log => LogManager.GetLogger(typeof(ParallelClusterClient)); + return null; } -} +} \ No newline at end of file diff --git a/homework 2/ClusterClient/Clients/RandomClusterClient.cs b/homework 2/ClusterClient/Clients/RandomClusterClient.cs index 4de13f3..3e44a0c 100644 --- a/homework 2/ClusterClient/Clients/RandomClusterClient.cs +++ b/homework 2/ClusterClient/Clients/RandomClusterClient.cs @@ -6,7 +6,7 @@ namespace ClusterClient.Clients { public class RandomClusterClient : ClusterClientBase { - private readonly Random random = new Random(); + private readonly Random _random = new(); public RandomClusterClient(string[] replicaAddresses) : base(replicaAddresses) @@ -15,10 +15,10 @@ public RandomClusterClient(string[] replicaAddresses) public override async Task ProcessRequestAsync(string query, TimeSpan timeout) { - var uri = ReplicaAddresses[random.Next(ReplicaAddresses.Length)]; + var baseUri = ReplicaAddresses[_random.Next(ReplicaAddresses.Length)]; + + var webRequest = CreateRequest(baseUri + "?query=" + query); - var webRequest = CreateRequest(uri + "?query=" + query); - Log.InfoFormat($"Processing {webRequest.RequestUri}"); var resultTask = ProcessRequestAsync(webRequest); diff --git a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs index 0293628..2a4082b 100644 --- a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs +++ b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs @@ -1,23 +1,40 @@ using System; -using System.Collections.Generic; +using System.Diagnostics; using System.Linq; -using System.Text; using System.Threading.Tasks; +using ClusterClient.Models; using log4net; -namespace ClusterClient.Clients +namespace ClusterClient.Clients; + +public class RoundRobinClusterClient : ClusterClientBase { - public class RoundRobinClusterClient : ClusterClientBase + protected override ILog Log => LogManager.GetLogger(typeof(RoundRobinClusterClient)); + + private readonly Instance[] _instances; + + public RoundRobinClusterClient(string[] replicaAddresses) : base(replicaAddresses) { - public RoundRobinClusterClient(string[] replicaAddresses) : base(replicaAddresses) - { - } + _instances = ReplicaAddresses.Select(address => new Instance(0, address)).ToArray(); + } + + public override async Task ProcessRequestAsync(string query, TimeSpan timeout) + { + var tasks = _instances + .OrderBy(s => s.Time) + .Select(s => CreateRequest(s.Address + "?query=" + query)) + .Select((req, i) => (task: TryProcessRequestAsync(req), i)); - public override Task ProcessRequestAsync(string query, TimeSpan timeout) + foreach (var (task, i) in tasks) { - throw new NotImplementedException(); + var sw = Stopwatch.StartNew(); + await Task.WhenAny(task, Task.Delay(timeout / (ReplicaAddresses.Length - i))); + sw.Stop(); + timeout -= TimeSpan.FromMilliseconds(sw.ElapsedMilliseconds); + _instances[i] = _instances[i] with { Time = sw.ElapsedMilliseconds }; + if (task.IsCompleted && task.Result is not null) return task.Result; } - protected override ILog Log => LogManager.GetLogger(typeof(RoundRobinClusterClient)); + throw new TimeoutException(); } -} +} \ No newline at end of file diff --git a/homework 2/ClusterClient/Clients/SmartClusterClient.cs b/homework 2/ClusterClient/Clients/SmartClusterClient.cs index eb06d8b..36160b3 100644 --- a/homework 2/ClusterClient/Clients/SmartClusterClient.cs +++ b/homework 2/ClusterClient/Clients/SmartClusterClient.cs @@ -1,23 +1,50 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; +using System.Diagnostics; using System.Threading.Tasks; +using ClusterClient.Models; using log4net; namespace ClusterClient.Clients { public class SmartClusterClient : ClusterClientBase { + protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient)); + + private readonly Instance[] _instances; + public SmartClusterClient(string[] replicaAddresses) : base(replicaAddresses) { + _instances = ReplicaAddresses.Select(address => new Instance(0, address)).ToArray(); } - public override Task ProcessRequestAsync(string query, TimeSpan timeout) + public override async Task ProcessRequestAsync(string query, TimeSpan timeout) { - throw new NotImplementedException(); - } + var tasks = _instances + .OrderBy(s => s.Time) + .Select(s => CreateRequest(s.Address + "?query=" + query)) + .Select((req, i) => (task: TryProcessRequestAsync(req), i)); + var previousTasks = new List>(); - protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient)); + foreach (var (t, i) in tasks) + { + previousTasks.Add(t); + var sw = Stopwatch.StartNew(); + var task = Task.WhenAny(previousTasks); + var delay = Task.Delay(timeout / (ReplicaAddresses.Length - i)); + await Task.WhenAny(task, delay); + sw.Stop(); + timeout -= TimeSpan.FromMilliseconds(sw.ElapsedMilliseconds); + _instances[i] = _instances[i] with {Time = sw.ElapsedMilliseconds}; + if (task.IsCompleted) + { + if (task.Result.Result is not null) return task.Result.Result; + previousTasks.Remove(task.Result); + }; + } + + throw new TimeoutException(); + } } -} +} \ No newline at end of file diff --git a/homework 2/ClusterClient/ClusterClient.csproj b/homework 2/ClusterClient/ClusterClient.csproj index b99eac3..771683e 100644 --- a/homework 2/ClusterClient/ClusterClient.csproj +++ b/homework 2/ClusterClient/ClusterClient.csproj @@ -1,6 +1,6 @@  - net8.0 + net9.0 Exe ..\ true diff --git a/homework 2/ClusterClient/Models/Instance.cs b/homework 2/ClusterClient/Models/Instance.cs new file mode 100644 index 0000000..c9aecff --- /dev/null +++ b/homework 2/ClusterClient/Models/Instance.cs @@ -0,0 +1,3 @@ +namespace ClusterClient.Models; + +public record Instance(long Time, string Address); \ No newline at end of file diff --git a/homework 2/ClusterServer/ClusterServer.csproj b/homework 2/ClusterServer/ClusterServer.csproj index 4bf0c96..9460690 100644 --- a/homework 2/ClusterServer/ClusterServer.csproj +++ b/homework 2/ClusterServer/ClusterServer.csproj @@ -1,6 +1,6 @@  - net8.0 + net9.0 Exe ..\ true diff --git a/homework 2/ClusterServer/Program.cs b/homework 2/ClusterServer/Program.cs index c1891a4..548d248 100644 --- a/homework 2/ClusterServer/Program.cs +++ b/homework 2/ClusterServer/Program.cs @@ -19,19 +19,19 @@ public static void Main(string[] args) if(!ServerOptions.TryGetArguments(args, out var parsedArguments)) return; - var server = new ClusterServer(parsedArguments, log); + var server = new ClusterServer(parsedArguments, Log); server.Start(); - log.InfoFormat("Press ENTER to stop listening"); + Log.InfoFormat("Press ENTER to stop listening"); Console.ReadLine(); - log.InfoFormat("Server stopped!"); + Log.InfoFormat("Server stopped!"); } catch(Exception e) { - log.Fatal(e); + Log.Fatal(e); } } - private static readonly ILog log = LogManager.GetLogger(typeof(Program)); + private static readonly ILog Log = LogManager.GetLogger(typeof(Program)); } } \ No newline at end of file diff --git a/homework 2/ClusterTests/ClusterTests.csproj b/homework 2/ClusterTests/ClusterTests.csproj index fcb9a04..8867ac9 100644 --- a/homework 2/ClusterTests/ClusterTests.csproj +++ b/homework 2/ClusterTests/ClusterTests.csproj @@ -1,6 +1,6 @@  - net8.0 + net9.0 Exe false