Command 패턴

Eunho Bae·2022년 6월 26일

Command 패턴

ClientSession에서 온 요청을 바로 실행하는 것이 아니라 job이라는 개념으로 캡슐화한 후 나중에 처리하는 방식으로 lock을 최소화 할 수 있다.

Server::Job

public interface IJob
	{
		void Execute();
	}

	public class Job : IJob // 매개변수 x
	{
		Action _action;

		public Job(Action action)
		{
			_action = action;
		}

		public void Execute()
		{
			_action.Invoke();
		}
	}

	public class Job<T1> : IJob // 매개변수 1개인 함수
	{
		Action<T1> _action;
		T1 _t1; // 매개변수는 따로 관리

		public Job(Action<T1> action, T1 t1)
		{
			_action = action;
			_t1 = t1;
		}

		public void Execute()
		{
			_action.Invoke(_t1);
		}
	}

	public class Job<T1, T2> : IJob
	{
		Action<T1, T2> _action;
		T1 _t1;
		T2 _t2;

		public Job(Action<T1, T2> action, T1 t1, T2 t2)
		{
			_action = action;
			_t1 = t1;
			_t2 = t2;
		}

		public void Execute()
		{
			_action.Invoke(_t1, _t2);
		}
	}

Server::JobSerializer

public class JobSerializer
	{
		Queue<IJob> _jobQueue = new Queue<IJob>();
		object _lock = new object();
		bool _flush = false;

		public void Push(Action action) { Push(new Job(action)); }
		public void Push<T1>(Action<T1> action, T1 t1) { Push(new Job<T1>(action, t1)); }
		public void Push<T1, T2>(Action<T1, T2> action, T1 t1, T2 t2) { Push(new Job<T1, T2>(action, t1, t2)); }
		public void Push<T1, T2, T3>(Action<T1, T2, T3> action, T1 t1, T2 t2, T3 t3) { Push(new Job<T1, T2, T3>(action, t1, t2, t3)); }

		public void Push(IJob job)
		{
			bool flush = false;

			lock (_lock)
			{
				_jobQueue.Enqueue(job);
				if (_flush == false) // 내가 처음으로 실행한 경우
					flush = _flush = true;
			}

			if (flush)
				Flush(); // job을 처리하는 중에 Push()가 호출되면 job만 던져놓음
		}

		void Flush()
		{
			while (true)
			{
				IJob job = Pop();
				if (job == null)
					return;

				job.Execute();
			}
		}

		IJob Pop()
		{
			lock (_lock)
			{
				if (_jobQueue.Count == 0)
				{
					_flush = false;
					return null;
				}
				return _jobQueue.Dequeue();
			}
		}
	}

Push를 하면서 jobQueue에 일감을 넣어줄 수 있다.
맨 처음 jobQueue에 일감을 넣어주는 경우, _flush가 false이기 때문에 true로 바꿔준 후 Flush() 함수를 호출하여 하나뿐인 일감을 꺼내서 그 일감을 실행할 것이다.
실행 중인 사이에 다른 일감이 10개씩 밀려 들어왔다면 그 10개의 일감은 _flush가 true이기 때문에 jobQueue에 넣어주는 것만 해주고 빠져나갈 것이고, 10개를 다 넣은 후 첫번째 일감이 실행 종료가 되면 11번째 일감이 왔을 때는 _flush가 다시 false이기 때문에, 자기자신을 포함한 11개의 일감을 Flush() 호출로 처리해 줄 것이다.

profile
개인 공부 정리

0개의 댓글