diff --git a/Tasks/Test4/Test4.Tests/PriorityQueueTests.cs b/Tasks/Test4/Test4.Tests/PriorityQueueTests.cs
new file mode 100644
index 0000000..dabb6de
--- /dev/null
+++ b/Tasks/Test4/Test4.Tests/PriorityQueueTests.cs
@@ -0,0 +1,160 @@
+// Copyright (c) Alexander Bugaev 2024
+//
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+namespace Test4.Tests;
+
+///
+/// Tests for the priority queue implementation.
+///
+public static class PriorityQueueTests
+{
+ ///
+ /// Tests adding elements to the queue ny checking the queue size.
+ ///
+ [Test]
+ public static void Test_EnqueueAndCheckSize_SizeIsUpdated()
+ {
+ var queue = new PriorityQueue();
+ Assert.That(queue.Size, Is.EqualTo(0));
+ queue.Enqueue(20, 1);
+ Assert.That(queue.Size, Is.EqualTo(1));
+ queue.Enqueue(2, 100);
+ Assert.That(queue.Size, Is.EqualTo(2));
+ }
+
+ ///
+ /// Tests adding elements to the queue by obtaining max priority elements.
+ ///
+ [Test]
+ public static void Test_EnqueueAndDequeue_ElementsAreObtainedProperly()
+ {
+ var queue = new PriorityQueue();
+ queue.Enqueue("qwew", 12f);
+ queue.Enqueue("0", 54f);
+ queue.Enqueue("ioio", 12f);
+ queue.Enqueue("abc", 0);
+ Assert.That(queue.Dequeue(), Is.EqualTo("0"));
+ Assert.That(queue.Size, Is.EqualTo(3));
+ Assert.That(queue.Dequeue(), Is.EqualTo("qwew"));
+ Assert.That(queue.Dequeue(), Is.EqualTo("ioio"));
+ Assert.That(queue.Dequeue(), Is.EqualTo("abc"));
+ Assert.That(queue.Size, Is.EqualTo(0));
+ }
+
+ ///
+ /// Tests the concurrent element adding to the queue.
+ ///
+ [Test]
+ public static void Test_AddElementsConcurrently_ElementsAreAddedProperly()
+ {
+ var queue = new PriorityQueue();
+ var firstThread = new Thread(() =>
+ {
+ queue.Enqueue(12, "a");
+ queue.Enqueue(990, "c");
+ queue.Enqueue(12, "dc");
+ });
+ var secondThread = new Thread(() =>
+ {
+ queue.Enqueue(-100, "d");
+ queue.Enqueue(0, "b");
+ queue.Enqueue(190, "ab");
+ });
+ firstThread.Start();
+ secondThread.Start();
+ JoinThreads([firstThread, secondThread]);
+ Assert.That(queue.Size, Is.EqualTo(6));
+ Assert.That(queue.Dequeue(), Is.EqualTo(12));
+ Assert.That(queue.Dequeue(), Is.EqualTo(-100));
+ Assert.That(queue.Dequeue(), Is.EqualTo(990));
+ Assert.That(queue.Size, Is.EqualTo(3));
+ }
+
+ ///
+ /// Tests the concurrent elements adding to the queue.
+ ///
+ [Test]
+ public static void Test_AddElementsFromManyThreads_ElementsAreAddedProperly()
+ {
+ var numberOfThreads = 12;
+ var threads = new Thread[numberOfThreads];
+ var queue = new PriorityQueue();
+ for (int i = 0; i < numberOfThreads; ++i)
+ {
+ var localI = i + 1;
+ threads[i] = new Thread(() =>
+ {
+ Thread.Sleep(1000);
+ queue.Enqueue(localI, localI);
+ });
+ threads[i].Start();
+ }
+
+ JoinThreads(threads);
+ Assert.That(queue.Size, Is.EqualTo(numberOfThreads));
+ Assert.That(queue.Dequeue(), Is.EqualTo(numberOfThreads));
+ Assert.That(queue.Dequeue(), Is.EqualTo(numberOfThreads - 1));
+ Assert.That(queue.Size, Is.EqualTo(numberOfThreads - 2));
+ }
+
+ ///
+ /// Tests the obtaining of elements form the queue in case of adding from other queue.
+ ///
+ [Test]
+ public static void Test_EnqueueAndDequeueFromDifferentThreads_ElementsAreObtainedProperly()
+ {
+ var queue = new PriorityQueue();
+ var firstThread = new Thread(() =>
+ {
+ Thread.Sleep(1000);
+ queue.Enqueue("fas", 231);
+ queue.Enqueue("2313211", 1);
+ queue.Enqueue("op", -1);
+ });
+ var secondThread = new Thread(() =>
+ {
+ Assert.That(queue.Size, Is.EqualTo(0));
+ Assert.That(queue.Dequeue(), Is.EqualTo("fas"));
+ Assert.That(queue.Dequeue(), Is.EqualTo("2313211"));
+ Assert.That(queue.Dequeue(), Is.EqualTo("op"));
+ Assert.That(queue.Size, Is.EqualTo(0));
+ });
+ firstThread.Start();
+ secondThread.Start();
+ JoinThreads([firstThread, secondThread]);
+ }
+
+ ///
+ /// Tests the concurrent element obtaining from the queue.
+ ///
+ [Test]
+ public static void Test_DequeueConcurrentlyFromManyThreads_ElementsAreObtainedProperly()
+ {
+ var numberOfThreads = 10;
+ var threads = new Thread[numberOfThreads];
+ var queue = new PriorityQueue();
+ for (int i = 0; i < numberOfThreads; ++i)
+ {
+ queue.Enqueue(42, 42);
+ threads[i] = new Thread(() =>
+ {
+ Thread.Sleep(1000);
+ Assert.That(queue.Dequeue(), Is.EqualTo(42));
+ });
+ threads[i].Start();
+ }
+
+ JoinThreads(threads);
+ }
+
+ private static void JoinThreads(IEnumerable threads)
+ {
+ foreach (var thread in threads)
+ {
+ thread.Join();
+ }
+ }
+}
diff --git a/Tasks/Test4/Test4.Tests/Test4.Tests.csproj b/Tasks/Test4/Test4.Tests/Test4.Tests.csproj
new file mode 100644
index 0000000..af8a78a
--- /dev/null
+++ b/Tasks/Test4/Test4.Tests/Test4.Tests.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net9.0
+ latest
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tasks/Test4/Test4.sln b/Tasks/Test4/Test4.sln
new file mode 100644
index 0000000..6ffdcb0
--- /dev/null
+++ b/Tasks/Test4/Test4.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test4", "Test4\Test4.csproj", "{648707F5-317C-4E02-AE4A-40A2BDED9F38}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test4.Tests", "Test4.Tests\Test4.Tests.csproj", "{9C4AA1E4-8D0F-4632-8D78-5FA4EE0F100A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {648707F5-317C-4E02-AE4A-40A2BDED9F38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {648707F5-317C-4E02-AE4A-40A2BDED9F38}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {648707F5-317C-4E02-AE4A-40A2BDED9F38}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {648707F5-317C-4E02-AE4A-40A2BDED9F38}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9C4AA1E4-8D0F-4632-8D78-5FA4EE0F100A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9C4AA1E4-8D0F-4632-8D78-5FA4EE0F100A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9C4AA1E4-8D0F-4632-8D78-5FA4EE0F100A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9C4AA1E4-8D0F-4632-8D78-5FA4EE0F100A}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/Tasks/Test4/Test4/PriorityQueue.cs b/Tasks/Test4/Test4/PriorityQueue.cs
new file mode 100644
index 0000000..59e44c2
--- /dev/null
+++ b/Tasks/Test4/Test4/PriorityQueue.cs
@@ -0,0 +1,91 @@
+// Copyright (c) Alexander Bugaev 2024
+//
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+namespace Test4;
+
+///
+/// Thread safe queue with priorities.
+///
+/// The type of the value stored in the queue.
+/// The type of the priorities used in the queue.
+public class PriorityQueue
+ where TPriority : IComparable
+{
+ private readonly object lockObject = new ();
+ private Element? head;
+
+ ///
+ /// Gets the size of the queue at some moment.
+ ///
+ public int Size { get; private set; }
+
+ ///
+ /// Inserts the given element with given priority to the queue.
+ ///
+ /// The value to insert.
+ /// The priority of the element.
+ public void Enqueue(TValue value, TPriority priority)
+ {
+ lock (this.lockObject)
+ {
+ var (previous, current) = this.GetElementByPriority(priority);
+ var elementToInsert = new Element(value, priority, current);
+ if (previous is not null)
+ {
+ previous.Next = elementToInsert;
+ }
+ else
+ {
+ this.head = elementToInsert;
+ }
+
+ ++this.Size;
+ Monitor.Pulse(this.lockObject);
+ }
+ }
+
+ ///
+ /// Gets the element with max priority from the queue.
+ ///
+ /// The value with the max priority in the queue.
+ public TValue Dequeue()
+ {
+ lock (this.lockObject)
+ {
+ while (this.Size == 0)
+ {
+ Monitor.Wait(this.lockObject);
+ }
+
+ ArgumentNullException.ThrowIfNull(this.head);
+ var valueToReturn = this.head.Value;
+ this.head = this.head.Next;
+ --this.Size;
+ return valueToReturn;
+ }
+ }
+
+ private (Element?, Element?) GetElementByPriority(TPriority priority)
+ {
+ Element? previous = null;
+ Element? current = this.head;
+ for (; current is not null && current.Priority.CompareTo(priority) >= 0; current = current.Next)
+ {
+ previous = current;
+ }
+
+ return (previous, current);
+ }
+
+ private class Element(TValue value, TPriority priority, Element? next = null)
+ {
+ public TValue Value { get; } = value;
+
+ public TPriority Priority { get; } = priority;
+
+ public Element? Next { get; set; } = next;
+ }
+}
diff --git a/Tasks/Test4/Test4/Test4.csproj b/Tasks/Test4/Test4/Test4.csproj
new file mode 100644
index 0000000..c7e645b
--- /dev/null
+++ b/Tasks/Test4/Test4/Test4.csproj
@@ -0,0 +1,10 @@
+
+
+ net9.0
+ enable
+ enable
+
+
+
+
+
\ No newline at end of file