stack/hopr

writing between 0 and 1

Archive for the ‘synchronization’ tag

Event Horizon

with one comment

Using Windows Event Objects to Sync / Queue Parallel Tasks

Windows Event Objects is a wonderful mechanism provided to sync a lot of things happening in parallel. Particularly, in controlling a lot of things happening in parallel. Recently, I did just the same.

The scenario here is piece of code which downloads a lot of files. But, this piece of code can be deadly as it can downloads a lot of files. It has virtually no limit. You ask it to download 10 files, it will, you ask for 100, it sure will. This may be good for the client, but may choke the server to death. That’s not very good for the server. Hence, we need to limit the number of files being downloaded to some decent number.

One option, is to fire a certain number of downloads and keep waiting on them in a loop. As soon as one finishes, start the next one. Only downside: the loop. Looping continuously without sleeping can be painful for the processor. We can sleep for say, 100ms, between consecutive polls. But, this adds latency between the time that one file finishes and the next one begins. That is, there could be a maximum delay of 100ms between one file finishing and the next one starting. Latency is a waste of time.

Using event objects takes out the latency part from the above polling logic. We create events for each of the downloading files and then wait on those events. The thread waiting on an event is unblocked immediately after an event becomes signaled (an event can be signaled by a downloading thread). Thus, no latency.

The sample program, events.cpp is demonstrating such a strategy. The function “parallel_download” takes a list of files to be downloaded and and the file count. Here, we’re only simulating a download, as doing a real download is out of scope of this algorithm. The thread “download_file” downloads (simulates downloading) a file.

Pasting function “parallel_download” here. The code + comments explain the logic.

void parallel_download(int file_list[], int file_count)
{

	HANDLE event_handle_list[max_parallel_downloads];
	vector<file_data> file_data_list;
	DWORD dret;

	//create a list of files to be downloaded
	for(int i = 0; i < file_count; i++)
	{

		file_data_list.push_back(file_data(i, file_list[i]));
	}

	int download_started_count;

	bool less_than_max_files = max_parallel_downloads > file_count;
	int max_start_count = less_than_max_files ? file_count : max_parallel_downloads;		//in case file count < max parallel downloads

	//first, start download of max permissible no. of files

	for(download_started_count = 0; download_started_count < max_start_count; download_started_count++)
	{

		event_handle_list[download_started_count] = file_data_list[download_started_count].event_handle;
		_beginthread(download_file, 0, (void*)(&file_data_list[download_started_count]));
	}

	//wait for any one file to finish if not less than max no. of files permissible
	dret = WaitForMultipleObjects(max_start_count, event_handle_list, less_than_max_files, 300000);		

	if(!less_than_max_files)
	{

		bool last_wait;
		//one of the files has finished downloading, so start another one
		while(download_started_count + 1 < file_count)
		{

			download_started_count++;

			event_handle_list[dret - WAIT_OBJECT_0] = file_data_list[download_started_count].event_handle;

			_beginthread(download_file, 0, (void*)(&file_data_list[download_started_count]));

			last_wait = (download_started_count + 1) == file_count;		//if last wait, wait for all to finish, else wait for any one to finish

			dret = WaitForMultipleObjects(max_start_count, event_handle_list, last_wait, 300000);
		}
	}

	cout<<"\n all files finished\n";
}

Using the sample program

1. Three test functions are provided: test1(), test2() and test3(), call them one by one or all at once to see different sync conditions.

2. Set the const “max_parallel_downloads” to the number you want to limit the donwlodas to. (default = 5).

Note

1. The critical section variable is being used to sync output to stdout. It’s not part of the algorithm. without a critical section, the output may get all garbled in presence of multiple verbose threads.

Written by Amol

April 7th, 2009 at 2:34 pm

Posted in code

Tagged with ,