-
Notifications
You must be signed in to change notification settings - Fork 0
Lazy #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Lazy #21
Changes from all commits
d089905
d2023b9
a1eed46
f9f3ba9
7f3b5f7
914f984
41fc3c9
93a167e
d40eae5
acfaf47
a325505
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| | ||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||
| # Visual Studio Version 17 | ||
| VisualStudioVersion = 17.4.33403.182 | ||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lazy", "Lazy\Lazy.csproj", "{F8C3CCDA-224A-4684-8E2E-B649BDBB08D3}" | ||
| EndProject | ||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestsForLazy", "TestsForLazy\TestsForLazy.csproj", "{A08651D9-B95A-4E33-A8B0-D5635E0523C8}" | ||
| EndProject | ||
| Global | ||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
| Debug|Any CPU = Debug|Any CPU | ||
| Release|Any CPU = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
| {F8C3CCDA-224A-4684-8E2E-B649BDBB08D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {F8C3CCDA-224A-4684-8E2E-B649BDBB08D3}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {F8C3CCDA-224A-4684-8E2E-B649BDBB08D3}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {F8C3CCDA-224A-4684-8E2E-B649BDBB08D3}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| {A08651D9-B95A-4E33-A8B0-D5635E0523C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
| {A08651D9-B95A-4E33-A8B0-D5635E0523C8}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
| {A08651D9-B95A-4E33-A8B0-D5635E0523C8}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
| {A08651D9-B95A-4E33-A8B0-D5635E0523C8}.Release|Any CPU.Build.0 = Release|Any CPU | ||
| EndGlobalSection | ||
| GlobalSection(SolutionProperties) = preSolution | ||
| HideSolutionNode = FALSE | ||
| EndGlobalSection | ||
| GlobalSection(ExtensibilityGlobals) = postSolution | ||
| SolutionGuid = {8BB48519-EA16-4F07-8949-4E4CC34A90F4} | ||
| EndGlobalSection | ||
| EndGlobal |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| namespace Lazy; | ||
|
|
||
| /// <summary> | ||
| /// The class implements an object whose function is called only once. | ||
| /// </summary> | ||
| public interface ILazy<T> | ||
| { | ||
| /// <summary> | ||
| /// Gets the created object. | ||
| /// </summary> | ||
| T? Get(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net7.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
| <OutputType>Library</OutputType> | ||
| </PropertyGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| namespace Lazy; | ||
|
|
||
| public class MultiThreadLazy<T> : ILazy<T> | ||
| { | ||
| private Func<T>? supplier; | ||
| private readonly Object objectLock = new (); | ||
| private volatile bool isCalculated = false; | ||
| private T? resultSuppiler; | ||
| private Exception? exceptionSuppiler = default; | ||
|
|
||
| /// <summary> | ||
| /// Constructor for storing the object creation function | ||
| /// </summary> | ||
| public MultiThreadLazy(Func<T> function) | ||
| { | ||
| supplier = function; | ||
| } | ||
|
|
||
| public T? Get() | ||
| { | ||
| if (!isCalculated) | ||
| { | ||
| lock (objectLock) | ||
| { | ||
| if (!isCalculated) | ||
| { | ||
| try | ||
| { | ||
| if (supplier == null) | ||
| { | ||
| resultSuppiler = default; | ||
| } | ||
| else | ||
| { | ||
| resultSuppiler = supplier(); | ||
| } | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Компилятор остался неуслышанным :( |
||
| catch (Exception ex) | ||
| { | ||
| exceptionSuppiler = ex; | ||
| } | ||
| finally | ||
| { | ||
| supplier = null; | ||
| } | ||
| isCalculated = true; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (exceptionSuppiler != default) | ||
| { | ||
| throw exceptionSuppiler; | ||
| } | ||
| return resultSuppiler; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| namespace Lazy; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. К однопоточной реализации те же замечания, что и к multiThread варианту There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. В этот раз аналогично |
||
|
|
||
| public class SingleThreadLazy<T> : ILazy<T> | ||
| { | ||
| private Func<T>? supplier; | ||
| private bool isCalculated = false; | ||
| private T? resultSuppiler; | ||
| private Exception? exceptionFromSuppiler = default; | ||
|
|
||
| /// <summary> | ||
| /// Constructor for storing the object creation function | ||
| /// </summary> | ||
| public SingleThreadLazy(Func<T> function) | ||
| { | ||
| supplier = function; | ||
| } | ||
|
|
||
| public T? Get() | ||
| { | ||
| if (!isCalculated) | ||
| { | ||
| try | ||
| { | ||
| if (supplier != null) | ||
| { | ||
| resultSuppiler = supplier(); | ||
| } | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| exceptionFromSuppiler = ex; | ||
| } | ||
| finally | ||
| { | ||
| supplier = null; | ||
| } | ||
| isCalculated = true; | ||
| } | ||
| if (exceptionFromSuppiler != default) | ||
| { | ||
| throw exceptionFromSuppiler; | ||
| } | ||
| return resultSuppiler; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| namespace Lazy; | ||
|
|
||
| /// <summary> | ||
| /// Class implements functions for checking Lazy | ||
| /// </summary> | ||
| public class FunctionsForTests | ||
| { | ||
| private int howMuchFunctionBeenCalled = 0; | ||
|
|
||
| /// <summary> | ||
| /// Function that counts how much it is caused by | ||
| /// </summary> | ||
| public int FunctionForLazyWithCounter() | ||
| { | ||
| Interlocked.Increment(ref howMuchFunctionBeenCalled); | ||
| return howMuchFunctionBeenCalled; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Function that throws DivideByZeroException | ||
| /// </summary> | ||
| /// <returns></returns> | ||
| public int DivideByZeroException() | ||
| { | ||
| var firstNumber = 1; | ||
| return firstNumber / 0; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| namespace TestsForLazy; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. В этом файле аналогично: надо сделать так, чтобы предупреждения компилятора исчезли |
||
| using Lazy; | ||
|
|
||
| public class Tests | ||
| { | ||
| private static readonly FunctionsForTests globalFunctionsForTestsForSingleThread = new(); | ||
| private static readonly FunctionsForTests globalFunctionsForTestsForMultiThread = new(); | ||
|
|
||
| [Test] | ||
| public void SimpleExampleWithMultiThreadLazy() | ||
| { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Тест с тремя+ Assert-ами не есть хорошо, особенно, если они все друг с другом не связаны. Надо разбить на более мелкие тесты с говорящими названиями
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Я понял, но тогда будут отдельные тесты для многопоточности и одно поточности, я не очень понимаю, как применить TestCaseSource, если запуск многопоточных тест и однопоточных сильно отличается There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Речь больше про то, чтобы разделить тесты на два типа:
|
||
| var functionsForTests = new FunctionsForTests(); | ||
| var multiThreadLazy = new MultiThreadLazy<int>(() => functionsForTests.FunctionForLazyWithCounter()); | ||
| var arrayThreads = new Thread[10]; | ||
|
|
||
| var arrayResult = new int[10]; | ||
|
|
||
| for (int i = 0; i < arrayThreads.Length; i++) | ||
| { | ||
| var local = i; | ||
| arrayThreads[i] = new Thread(() => | ||
| { | ||
| for (var j = local; j < 10; j++) | ||
| { | ||
| arrayResult[j] = multiThreadLazy.Get(); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| foreach (var thread in arrayThreads) | ||
| { | ||
| thread.Start(); | ||
| } | ||
|
|
||
| foreach (var element in arrayThreads) | ||
| { | ||
| element.Join(); | ||
| } | ||
|
|
||
| foreach (var element in arrayResult) | ||
| { | ||
| Assert.That(element, Is.EqualTo(1)); | ||
| } | ||
| } | ||
|
|
||
| [TestCaseSource(nameof(LazyForTestWithFunctionWithCounter))] | ||
| public void SimpleExampleWithOneThread(ILazy<int> lazy) | ||
| { | ||
| for (int i = 0; i < 10; ++i) | ||
| { | ||
| Assert.That(lazy.Get(), Is.EqualTo(1)); | ||
| } | ||
| } | ||
|
|
||
| [TestCaseSource(nameof(LazyForTestWithFunctionWithException))] | ||
| public void ExampleWithExceptionWithOneThread(ILazy<int> lazy) | ||
| { | ||
| for (int i = 0; i < 10; ++i) | ||
| { | ||
| Assert.Throws<DivideByZeroException>(() => lazy.Get()); | ||
| } | ||
| } | ||
|
|
||
| [Test] | ||
| public void ThreadRaceTest() | ||
| { | ||
| var functionsForTests = new FunctionsForTests(); | ||
| var multiThreadLazy = new MultiThreadLazy<int>(() => functionsForTests.FunctionForLazyWithCounter()); | ||
| var arrayThreads = new Thread[10]; | ||
| var arrayResult = new int[10]; | ||
|
|
||
| for (int i = 0; i < arrayThreads.Length; i++) | ||
| { | ||
| var local = i; | ||
| arrayThreads[i] = new Thread(() => | ||
| { | ||
| for (var j = local; j < 10; j++) | ||
| { | ||
| arrayResult[j] = multiThreadLazy.Get(); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| foreach (var element in arrayThreads) | ||
| { | ||
| element.Start(); | ||
| } | ||
|
|
||
| foreach (var element in arrayThreads) | ||
| { | ||
| element.Join(); | ||
| } | ||
|
|
||
| foreach (var element in arrayResult) | ||
| { | ||
| Assert.That(element, Is.EqualTo(1)); | ||
| } | ||
| } | ||
|
|
||
| private static IEnumerable<TestCaseData> LazyForTestWithFunctionWithCounter | ||
| => new TestCaseData[] | ||
| { | ||
| new TestCaseData(new MultiThreadLazy<int>(() => globalFunctionsForTestsForSingleThread.FunctionForLazyWithCounter())), | ||
| new TestCaseData(new SingleThreadLazy<int>(() => globalFunctionsForTestsForMultiThread.FunctionForLazyWithCounter())), | ||
| }; | ||
|
|
||
| private static IEnumerable<TestCaseData> LazyForTestWithFunctionWithException | ||
| => new TestCaseData[] | ||
| { | ||
| new TestCaseData(new SingleThreadLazy<int>(() => globalFunctionsForTestsForSingleThread.DivideByZeroException())), | ||
| new TestCaseData(new MultiThreadLazy<int>(() => globalFunctionsForTestsForMultiThread.DivideByZeroException())), | ||
| }; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net7.0</TargetFramework> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <Nullable>enable</Nullable> | ||
|
|
||
| <IsPackable>false</IsPackable> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> | ||
| <PackageReference Include="NUnit" Version="3.13.3" /> | ||
| <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" /> | ||
| <PackageReference Include="NUnit.Analyzers" Version="3.3.0" /> | ||
| <PackageReference Include="coverlet.collector" Version="3.1.2" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\Lazy\Lazy.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| global using NUnit.Framework; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Тоже важное замечание компилятора, которое также отсылает к условию "supplier вправе вернуть null"