diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln
index f346cec27c..bc7a1bf35e 100644
--- a/framework/Volo.Abp.sln
+++ b/framework/Volo.Abp.sln
@@ -196,6 +196,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCor
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BackgroundJobs", "src\Volo.Abp.BackgroundJobs\Volo.Abp.BackgroundJobs.csproj", "{E6E0BBB5-48A7-4FDA-8A47-8B308BCD36AD}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BackgroundWorkers", "src\Volo.Abp.BackgroundWorkers\Volo.Abp.BackgroundWorkers.csproj", "{6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -550,6 +552,10 @@ Global
{E6E0BBB5-48A7-4FDA-8A47-8B308BCD36AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E6E0BBB5-48A7-4FDA-8A47-8B308BCD36AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E6E0BBB5-48A7-4FDA-8A47-8B308BCD36AD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -644,6 +650,7 @@ Global
{CAE68246-70A8-4E87-9B83-A9F7DA343E5E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{27C120C9-F618-4C1D-B959-8D0B048D0835} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{E6E0BBB5-48A7-4FDA-8A47-8B308BCD36AD} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {6C3E76B8-C4DA-4E74-9F8B-A8BC4C831722} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}
diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo.Abp.BackgroundWorkers.csproj b/framework/src/Volo.Abp.BackgroundWorkers/Volo.Abp.BackgroundWorkers.csproj
new file mode 100644
index 0000000000..61bc5af316
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo.Abp.BackgroundWorkers.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+ netstandard2.0
+ Volo.Abp.BackgroundWorkers
+ Volo.Abp.BackgroundWorkers
+ $(AssetTargetFallback);portable-net45+win8+wp8+wpa81;
+ false
+ false
+ false
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs
new file mode 100644
index 0000000000..0d2cb421ff
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/AbpBackgroundWorkersModule.cs
@@ -0,0 +1,17 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Modularity;
+using Volo.Abp.Threading;
+
+namespace Volo.Abp.BackgroundWorkers
+{
+ [DependsOn(
+ typeof(AbpThreadingModule)
+ )]
+ public class AbpBackgroundWorkersModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAssemblyOf();
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs
new file mode 100644
index 0000000000..b2ae62fc6e
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerBase.cs
@@ -0,0 +1,47 @@
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Volo.Abp.Threading;
+
+namespace Volo.Abp.BackgroundWorkers
+{
+ ///
+ /// Base class that can be used to implement .
+ ///
+ public abstract class BackgroundWorkerBase : RunnableBase, IBackgroundWorker
+ {
+ //TODO: Add UOW, Localization and other useful properties..?
+
+ public ILogger Logger { protected get; set; }
+
+ protected BackgroundWorkerBase()
+ {
+ Logger = NullLogger.Instance;
+ }
+
+ public override void Start()
+ {
+ Logger.LogDebug("Starting background worker: " + ToString());
+ base.Start();
+ Logger.LogDebug("Started background worker: " + ToString());
+ }
+
+ public override void Stop()
+ {
+ Logger.LogDebug("Stopping background worker: " + ToString());
+ base.Stop();
+ Logger.LogDebug("Stopped background worker: " + ToString());
+ }
+
+ public override void WaitToStop()
+ {
+ Logger.LogDebug("Waiting background worker to completely stop: " + ToString());
+ base.WaitToStop();
+ Logger.LogDebug("Background worker is completely stopped: " + ToString());
+ }
+
+ public override string ToString()
+ {
+ return GetType().FullName;
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerManager.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerManager.cs
new file mode 100644
index 0000000000..6c5f890f12
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/BackgroundWorkerManager.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Threading;
+
+namespace Volo.Abp.BackgroundWorkers
+{
+ ///
+ /// Implements .
+ ///
+ public class BackgroundWorkerManager : RunnableBase, IBackgroundWorkerManager, ISingletonDependency, IDisposable
+ {
+ private readonly List _backgroundJobs;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BackgroundWorkerManager()
+ {
+ _backgroundJobs = new List();
+ }
+
+ public override void Start()
+ {
+ base.Start();
+
+ _backgroundJobs.ForEach(job => job.Start());
+ }
+
+ public override void Stop()
+ {
+ _backgroundJobs.ForEach(job => job.Stop());
+
+ base.Stop();
+ }
+
+ public override void WaitToStop()
+ {
+ _backgroundJobs.ForEach(job => job.WaitToStop());
+
+ base.WaitToStop();
+ }
+
+ public void Add(IBackgroundWorker worker)
+ {
+ _backgroundJobs.Add(worker);
+
+ if (IsRunning)
+ {
+ worker.Start();
+ }
+ }
+
+ private bool _isDisposed;
+
+ public void Dispose()
+ {
+ if (_isDisposed)
+ {
+ return;
+ }
+
+ _isDisposed = true;
+
+ //TODO: ???
+ }
+ }
+}
diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorker.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorker.cs
new file mode 100644
index 0000000000..1aec158155
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorker.cs
@@ -0,0 +1,12 @@
+using Volo.Abp.Threading;
+
+namespace Volo.Abp.BackgroundWorkers
+{
+ ///
+ /// Interface for a worker (thread) that runs on background to perform some tasks.
+ ///
+ public interface IBackgroundWorker : IRunnable
+ {
+
+ }
+}
diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorkerManager.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorkerManager.cs
new file mode 100644
index 0000000000..8465586cb3
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/IBackgroundWorkerManager.cs
@@ -0,0 +1,18 @@
+using Volo.Abp.Threading;
+
+namespace Volo.Abp.BackgroundWorkers
+{
+ ///
+ /// Used to manage background workers.
+ ///
+ public interface IBackgroundWorkerManager : IRunnable
+ {
+ ///
+ /// Adds a new worker. Starts the worker immediately if has started.
+ ///
+ ///
+ /// The worker. It should be resolved from IOC.
+ ///
+ void Add(IBackgroundWorker worker);
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs
new file mode 100644
index 0000000000..38d8448c72
--- /dev/null
+++ b/framework/src/Volo.Abp.BackgroundWorkers/Volo/Abp/BackgroundWorkers/PeriodicBackgroundWorkerBase.cs
@@ -0,0 +1,62 @@
+using System;
+using Microsoft.Extensions.Logging;
+using Volo.Abp.Threading;
+
+namespace Volo.Abp.BackgroundWorkers
+{
+ ///
+ /// Extends to add a periodic running Timer.
+ ///
+ public abstract class PeriodicBackgroundWorkerBase : BackgroundWorkerBase
+ {
+ protected readonly AbpTimer Timer;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A timer.
+ protected PeriodicBackgroundWorkerBase(AbpTimer timer)
+ {
+ Timer = timer;
+ Timer.Elapsed += Timer_Elapsed;
+ }
+
+ public override void Start()
+ {
+ base.Start();
+ Timer.Start();
+ }
+
+ public override void Stop()
+ {
+ Timer.Stop();
+ base.Stop();
+ }
+
+ public override void WaitToStop()
+ {
+ Timer.WaitToStop();
+ base.WaitToStop();
+ }
+
+ ///
+ /// Handles the Elapsed event of the Timer.
+ ///
+ private void Timer_Elapsed(object sender, System.EventArgs e)
+ {
+ try
+ {
+ DoWork();
+ }
+ catch (Exception ex)
+ {
+ Logger.LogException(ex);
+ }
+ }
+
+ ///
+ /// Periodic works should be done by implementing this method.
+ ///
+ protected abstract void DoWork();
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs
new file mode 100644
index 0000000000..c61dd645f4
--- /dev/null
+++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/AbpTimer.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Threading;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.Threading
+{
+ ///
+ /// A roboust timer implementation that ensures no overlapping occurs. It waits exactly specified between ticks.
+ ///
+ public class AbpTimer : RunnableBase, ITransientDependency
+ {
+ ///
+ /// This event is raised periodically according to Period of Timer.
+ ///
+ public event EventHandler Elapsed;
+
+ ///
+ /// Task period of timer (as milliseconds).
+ ///
+ public int Period { get; set; }
+
+ ///
+ /// Indicates whether timer raises Elapsed event on Start method of Timer for once.
+ /// Default: False.
+ ///
+ public bool RunOnStart { get; set; }
+
+ ///
+ /// This timer is used to perfom the task at spesified intervals.
+ ///
+ private readonly Timer _taskTimer;
+
+ ///
+ /// Indicates that whether timer is running or stopped.
+ ///
+ private volatile bool _running;
+
+ ///
+ /// Indicates that whether performing the task or _taskTimer is in sleep mode.
+ /// This field is used to wait executing tasks when stopping Timer.
+ ///
+ private volatile bool _performingTasks;
+
+ ///
+ /// Creates a new Timer.
+ ///
+ public AbpTimer()
+ {
+ _taskTimer = new Timer(TimerCallBack, null, Timeout.Infinite, Timeout.Infinite);
+ }
+
+ ///
+ /// Starts the timer.
+ ///
+ public override void Start()
+ {
+ if (Period <= 0)
+ {
+ throw new AbpException("Period should be set before starting the timer!");
+ }
+
+ base.Start();
+
+ _running = true;
+ _taskTimer.Change(RunOnStart ? 0 : Period, Timeout.Infinite);
+ }
+
+ ///
+ /// Stops the timer.
+ ///
+ public override void Stop()
+ {
+ lock (_taskTimer)
+ {
+ _running = false;
+ _taskTimer.Change(Timeout.Infinite, Timeout.Infinite);
+ }
+
+ base.Stop();
+ }
+
+ ///
+ /// Waits the service to stop.
+ ///
+ public override void WaitToStop()
+ {
+ lock (_taskTimer)
+ {
+ while (_performingTasks)
+ {
+ Monitor.Wait(_taskTimer);
+ }
+ }
+
+ base.WaitToStop();
+ }
+
+ ///
+ /// This method is called by _taskTimer.
+ ///
+ /// Not used argument
+ private void TimerCallBack(object state)
+ {
+ lock (_taskTimer)
+ {
+ if (!_running || _performingTasks)
+ {
+ return;
+ }
+
+ _taskTimer.Change(Timeout.Infinite, Timeout.Infinite);
+ _performingTasks = true;
+ }
+
+ try
+ {
+ if (Elapsed != null)
+ {
+ Elapsed(this, new EventArgs());
+ }
+ }
+ catch
+ {
+
+ }
+ finally
+ {
+ lock (_taskTimer)
+ {
+ _performingTasks = false;
+ if (_running)
+ {
+ _taskTimer.Change(Period, Timeout.Infinite);
+ }
+
+ Monitor.Pulse(_taskTimer);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/IRunnable.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/IRunnable.cs
new file mode 100644
index 0000000000..6aa5b2d1e4
--- /dev/null
+++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/IRunnable.cs
@@ -0,0 +1,25 @@
+namespace Volo.Abp.Threading
+{
+ ///
+ /// Interface to start/stop self threaded services.
+ ///
+ public interface IRunnable
+ {
+ ///
+ /// Starts the service.
+ ///
+ void Start();
+
+ ///
+ /// Sends stop command to the service.
+ /// Service may return immediately and stop asynchronously.
+ /// A client should then call method to ensure it's stopped.
+ ///
+ void Stop();
+
+ ///
+ /// Waits the service to stop.
+ ///
+ void WaitToStop();
+ }
+}
diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableBase.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableBase.cs
new file mode 100644
index 0000000000..bc72baeddd
--- /dev/null
+++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableBase.cs
@@ -0,0 +1,29 @@
+namespace Volo.Abp.Threading
+{
+ ///
+ /// Base implementation of .
+ ///
+ public abstract class RunnableBase : IRunnable
+ {
+ ///
+ /// A boolean value to check if this is running.
+ ///
+ public bool IsRunning => _isRunning;
+ private volatile bool _isRunning;
+
+ public virtual void Start()
+ {
+ _isRunning = true;
+ }
+
+ public virtual void Stop()
+ {
+ _isRunning = false;
+ }
+
+ public virtual void WaitToStop()
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableExtensions.cs b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableExtensions.cs
new file mode 100644
index 0000000000..91e423b15b
--- /dev/null
+++ b/framework/src/Volo.Abp.Threading/Volo/Abp/Threading/RunnableExtensions.cs
@@ -0,0 +1,17 @@
+namespace Volo.Abp.Threading
+{
+ ///
+ /// Some extension methods for .
+ ///
+ public static class RunnableExtensions
+ {
+ ///
+ /// Calls and then .
+ ///
+ public static void StopAndWaitToStop(this IRunnable runnable)
+ {
+ runnable.Stop();
+ runnable.WaitToStop();
+ }
+ }
+}
\ No newline at end of file