Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions Lazyy/Lazyy.Test/LazyTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Threading;
using NUnit.Framework;

namespace Lazyy.Test
{
public class SingleThreadTest
{
private static IEnumerable<TestCaseData> Lazy()
{
int countForSingle = 0;
int countForMulti = 0;
yield return new TestCaseData(LazyFactory.CreateSingleLazy(() => ++countForSingle));
yield return new TestCaseData(LazyFactory.CreateMultiLazy(() => Interlocked.Increment(ref countForMulti)));
}

[Test]
public void SupplierCannotBeNull()
{
Assert.Throws<ArgumentNullException>(() => LazyFactory.CreateSingleLazy<int>(null));
Assert.Throws<ArgumentNullException>(() => LazyFactory.CreateMultiLazy<int>(null));
}

[TestCaseSource(nameof(Lazy))]
public void TestLazyLaunchFunctionOnly1Time<T>(ILazy<T> lazy)
{
for (int i = 0; i < 100; i++)
{
Assert.AreEqual(1, lazy.Get());
}
}

[TestCaseSource(nameof(Lazy))]
public void GetShouldNotChangeValue<T>(ILazy<T> lazy)
{
var value = lazy.Get();
Assert.AreEqual(1, value);
for (int i = 0; i < 25; i++)
{
Assert.AreEqual(value, lazy.Get());
}
}

[Test]
public void RaceConditionsCheck()
{
var count = 0;
var lazy = LazyFactory.CreateMultiLazy(() => Interlocked.Increment(ref count));
var threads = new Thread[10];

for (int i = 0; i < threads.Length; ++i)
{
threads[i] = new Thread(() => lazy.Get());
}

foreach (var thread in threads)
{
thread.Start();
}
foreach (var thread in threads)
{
thread.Join();
}

Assert.AreEqual(1, count);
}
}
}
20 changes: 20 additions & 0 deletions Lazyy/Lazyy.Test/Lazyy.Test.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="coverlet.collector" Version="3.0.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Lazyy\Lazyy.csproj" />
</ItemGroup>

</Project>
22 changes: 22 additions & 0 deletions Lazyy/Lazyy.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lazyy", "Lazyy\Lazyy.csproj", "{DB81654E-7FB5-4DF1-BEB0-8E701E1A3A6A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lazyy.Test", "Lazyy.Test\Lazyy.Test.csproj", "{96805220-7A8C-4502-9503-91EB73541008}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DB81654E-7FB5-4DF1-BEB0-8E701E1A3A6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DB81654E-7FB5-4DF1-BEB0-8E701E1A3A6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DB81654E-7FB5-4DF1-BEB0-8E701E1A3A6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DB81654E-7FB5-4DF1-BEB0-8E701E1A3A6A}.Release|Any CPU.Build.0 = Release|Any CPU
{96805220-7A8C-4502-9503-91EB73541008}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96805220-7A8C-4502-9503-91EB73541008}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96805220-7A8C-4502-9503-91EB73541008}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96805220-7A8C-4502-9503-91EB73541008}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
13 changes: 13 additions & 0 deletions Lazyy/Lazyy/ILazy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Lazyy
{
/// <summary>
/// интерфейс ленивого вычисления
/// </summary>
public interface ILazy<out T>
{
/// <summary>
/// вызывает вычисление один раз и возвращает один и тот же обьект, полученный при вычислении
/// </summary>
public T Get();

This comment was marked as resolved.

}
}
22 changes: 22 additions & 0 deletions Lazyy/Lazyy/LazyFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;

namespace Lazyy
{
/// <summary>
/// создает обьекты для работы либо в однопоточном, либо в многопоточном режиме
/// </summary>
public static class LazyFactory
{
/// <summary>
/// создает обьект в однопоточном режиме
/// </summary>
public static ILazy<T> CreateSingleLazy<T>(Func<T> supplier)
=> new LazySingle<T>(supplier);

/// <summary>
/// создает обьект в многопоточном режиме
/// </summary>
public static ILazy<T> CreateMultiLazy<T>(Func<T> supplier)
=> new LazyMulti<T>(supplier);
}
}
46 changes: 46 additions & 0 deletions Lazyy/Lazyy/LazyMulti.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.Threading;

namespace Lazyy
{
/// <summary>
/// реализация ILazy в многопоточном режиме
/// </summary>
public class LazyMulti<T> : ILazy<T>
{
private T _value;
private bool _isGenerated = false;
private Func<T> _supplier;
private readonly Object _lockObject = new();

/// <summary>
/// создает обьект в многопоточном режиме
/// </summary>
public LazyMulti(Func<T> supplier)
=> _supplier = supplier ?? throw new ArgumentNullException();

/// <summary>
/// вызывает вычисление один раз и возвращает один и тот же обьект, полученный при вычислении
/// </summary>
public T Get()
{
if (_isGenerated)
{
return _value;
}

lock (_lockObject)
{
if (Volatile.Read(ref _isGenerated))
{
return _value;
}

_value = _supplier();

This comment was marked as resolved.

This comment was marked as resolved.

Volatile.Write(ref _isGenerated, true);
_supplier = null;
return _value;
}
}
}
}
36 changes: 36 additions & 0 deletions Lazyy/Lazyy/LazySingle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;

namespace Lazyy
{
/// <summary>
/// реализация ILazy в однопоточном режиме
/// </summary>
public class LazySingle<T> : ILazy<T>
{
private T _value;
private Func<T> _supplier;
private bool _isGenerated = false;

/// <summary>
/// создает обьект в однопоточном режиме
/// </summary>
public LazySingle(Func<T> supplier)
=> _supplier = supplier ?? throw new ArgumentNullException();

/// <summary>
/// вызывает вычисление один раз и возвращает один и тот же обьект, полученный при вычислении
/// </summary>
public T Get()
{
if (_isGenerated)
{
return _value;
}

_isGenerated = true;
_value = _supplier();
_supplier = null;
return _value;
}
}
}
9 changes: 9 additions & 0 deletions Lazyy/Lazyy/Lazyy.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<DockerDefaultTargetOS>Windows</DockerDefaultTargetOS>
</PropertyGroup>

</Project>
48 changes: 48 additions & 0 deletions Lazyy/Lazyy/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Threading;

namespace Lazyy
{
class Program
{
static void Main(string[] args)
{
var number1 = 2;
var number2 = 2;
var single = LazyFactory.CreateMultiLazy<int>(() =>
{
number1 += number1;
return number1;
});
var multi = LazyFactory.CreateMultiLazy<int>(() =>
{
number2 *= number2;
return number2;
});

var threads = new Thread[5];
for (int i = 0; i < threads.Length; i++)
{
threads[i] = new Thread(() => multi.Get());
}

foreach (var t in threads)
{
Console.WriteLine(multi.Get());
Console.WriteLine("!!!");
Console.WriteLine(single.Get());
Console.WriteLine("!!!");
}

foreach (var thread in threads)
{
thread.Start();
}

foreach (var thread in threads)
{
thread.Join();
}
}
}
}